React 是什么?
- React 是一个用于构建用户界面的 JavaScript 库,核心是组件化、声明式 UI 和状态驱动视图。
- React 只关心视图层,本身不强制路由、请求、状态管理方案。
- 它适合把复杂页面拆成多个可复用组件,再通过数据变化驱动页面更新。
React 的核心思想有哪些?
- 组件化:把页面拆成独立组件,降低维护成本。
- 声明式:描述 UI 的最终状态,不直接命令式操作 DOM。
- 单向数据流:父组件通过 props 向下传递数据,子组件通过回调通知父组件。
- 状态驱动视图:state 或 props 变化后,React 重新计算 UI。
React 和 Vue 的区别?
- React 更偏函数和 JSX 表达能力,Vue 更偏模板和渐进式框架体验。
- React 官方生态相对克制,路由、状态、请求多由社区方案组合。
- Vue 提供更多内置约定,比如模板语法、指令、SFC、响应式系统等。
- 面试时不要说谁更好,重点说团队技术栈、项目复杂度和生态选择。
JSX 是什么?
- JSX 是 JavaScript 的语法扩展,可以在 JS 中写类似 HTML 的结构。
- JSX 最终会被编译成 React 元素创建逻辑,不是浏览器原生语法。
- JSX 可以直接使用变量、表达式和函数,因此组合能力很强。
JSX 中为什么要用 className?
class是 JavaScript 保留字,React 使用className映射 HTML 的 class 属性。- 类似的还有
htmlFor,对应 label 的for属性。
React 元素和 React 组件有什么区别?
- React 元素是描述 UI 的普通对象,比如
<div />的结果。 - React 组件是返回 React 元素的函数或类。
- 组件可以接收 props、维护状态、组合其他组件。
props 和 state 的区别?
props是父组件传入的数据,组件内部不应该直接修改。state是组件自己的状态,可以通过setState或 Hook 更新。- props 更适合外部输入,state 更适合内部交互状态。
为什么不能直接修改 state?
- 直接修改对象不会改变引用,React 可能无法判断数据已经变化。
- 直接修改会破坏状态可预测性,调试和回溯都更困难。
- 正确方式是创建新对象或新数组,再调用 state 更新函数。
什么是单向数据流?
- 数据通常从父组件流向子组件。
- 子组件不能直接修改父组件数据,只能通过回调通知父组件更新。
- 好处是数据来源清晰,问题更容易定位。
为什么 React 组件名必须大写?
- 小写标签会被 React 当成原生 DOM 标签。
- 大写开头会被识别为自定义组件。
- 例如
<button />是 DOM 标签,<Button />是组件。
key 的作用是什么?
- key 用于帮助 React 识别列表中每一项的稳定身份。
- 当列表插入、删除、排序时,稳定 key 能减少错误复用和不必要更新。
- key 只在同层兄弟节点之间需要唯一,不要求全局唯一。
为什么不推荐用 index 做 key?
- 列表顺序变化时,index 会跟着变化,组件状态可能错位。
- 比如输入框列表用 index 做 key,删除一项后可能出现内容串位。
- 只有静态列表、不排序、不增删时,index 才相对安全。
什么是受控组件?
- 表单值由 React state 控制。
- 输入变化时通过
onChange更新 state。 - 优点是数据可控,适合校验、联动、提交前处理。
什么是非受控组件?
- 表单值由 DOM 自己维护,React 通常通过
ref获取值。 - 写法简单,适合简单表单或和第三方 DOM 库集成。
- 缺点是状态不完全由 React 管理,复杂交互不好控制。
Fragment 有什么用?
- Fragment 可以让组件返回多个相邻元素。
- 它不会额外生成 DOM 节点。
- 常用于列表、表格结构或避免无意义包裹层。
React Portal 是什么?
- Portal 可以把子节点渲染到父组件 DOM 层级以外的位置。
- 常用于 Modal、Popover、Tooltip、全局 Toast。
- 它不改变 React 组件树关系,事件冒泡仍按 React 树处理。
React 中事件机制有什么特点?
- React 使用合成事件,屏蔽不同浏览器事件差异。
- 事件处理函数通常写成
onClick={handleClick}。 - 不要在 render 中无脑创建复杂匿名函数,列表场景可能造成额外渲染压力。
React 什么时候会重新渲染?
- 组件自身 state 更新时会重新渲染。
- 父组件重新渲染时,子组件默认也会重新执行渲染。
- props、context 值变化也可能触发相关组件重新渲染。
重新渲染一定会更新真实 DOM 吗?
- 不一定。
- React 会先重新计算虚拟 DOM,再通过 diff 判断哪些真实 DOM 需要更新。
- 组件函数重新执行和真实 DOM 变化不是一回事。
Virtual DOM 的作用是什么?
- Virtual DOM 是 UI 的 JavaScript 对象描述。
- React 通过它比较新旧 UI,计算最小更新。
- 它的价值不是一定比手写 DOM 快,而是让 UI 更新更声明式、更可维护。
React diff 的核心规则是什么?
- 不同类型元素会被认为是不同树,通常直接替换。
- 同类型元素会比较属性和子节点。
- 列表子节点主要依赖 key 来判断复用关系。
什么是 reconciliation?
- reconciliation 是 React 根据新旧元素树计算更新的过程。
- 它决定组件是复用、更新、卸载还是重新创建。
- key、组件类型和节点位置都会影响 reconciliation 结果。
什么是 Fiber?
- Fiber 是 React 内部的协调数据结构。
- 它让渲染工作可以被拆分、暂停、恢复和按优先级调度。
- Concurrent Rendering、Suspense 等能力都依赖 Fiber 架构。
render 阶段和 commit 阶段有什么区别?
- render 阶段负责计算 UI 差异,可以被中断。
- commit 阶段负责把变更提交到 DOM,不能被中断。
- 副作用、DOM 更新、ref 更新都发生在 commit 相关阶段。
什么是批处理更新?
- React 会把同一事件或任务中的多次状态更新合并成一次渲染。
- 这样可以减少不必要的重复计算和 DOM 更新。
- React 18 以后,异步场景中的自动批处理能力更强。
setState 后为什么不能马上读到最新值?
- state 更新可能被批处理,不一定同步提交。
- 如果下一次更新依赖上一次 state,应该使用函数式更新。
- 例如:
setCount(count => count + 1)。
函数式更新有什么用?
- 可以拿到 React 提供的最新 state。
- 避免闭包里读取到旧值。
- 适合连续加减、队列更新、依赖旧状态的场景。
StrictMode 有什么作用?
- StrictMode 只在开发环境启用额外检查。
- 它可以帮助发现不安全副作用、过时 API 和不符合规则的写法。
- 开发环境中某些函数会被额外调用,不代表生产环境也会这样。
Hooks 解决了什么问题?
- Hooks 让函数组件可以使用状态、副作用、上下文等能力。
- 它避免了 class 组件中 this、生命周期分散和逻辑复用困难的问题。
- 自定义 Hook 可以把可复用逻辑抽成函数。
Hooks 的使用规则是什么?
- 只能在函数组件或自定义 Hook 中调用。
- 只能在顶层调用,不能放在条件、循环、嵌套函数中。
- 规则的本质是保证每次渲染 Hook 调用顺序一致。
useState 适合管理什么状态?
- 适合组件内部简单状态,比如输入框、开关、当前 tab。
- 状态更新依赖旧值时使用函数式更新。
- 多个状态强相关时,可以考虑合并对象或改用
useReducer。
useEffect 适合做什么?
- 适合处理组件渲染后的副作用,比如请求、订阅、定时器、手动操作 DOM。
- Effect 不应该用来计算可以在 render 中直接得到的数据。
- 依赖数组要真实表达 Effect 使用到的响应式值。
useEffect 的清理函数什么时候执行?
- 组件卸载时会执行清理函数。
- 依赖变化时,会先执行上一次清理函数,再执行新的 Effect。
- 常用于取消订阅、清理定时器、取消请求。
useEffect 和 useLayoutEffect 的区别?
useEffect在浏览器绘制后执行,不阻塞页面展示。useLayoutEffect在浏览器绘制前同步执行,会阻塞绘制。- 测量布局、同步修改 DOM 样式时可用
useLayoutEffect,普通副作用优先用useEffect。
useMemo 有什么用?
useMemo用来缓存计算结果。- 适合昂贵计算、稳定引用、避免子组件无意义渲染。
- 不要为了所有普通变量都套
useMemo,它本身也有维护成本。
useCallback 有什么用?
useCallback用来缓存函数引用。- 常用于传给
React.memo子组件,或作为其他 Hook 的依赖。 - 如果子组件没有依赖引用稳定性,使用收益可能很小。
useRef 有什么用?
- 可以保存一个可变值,修改它不会触发渲染。
- 常用于获取 DOM、保存定时器 ID、保存上一次值。
- 不要把需要驱动 UI 的数据放进 ref,应该放 state。
useContext 有什么优缺点?
- 优点是解决跨层级传值,适合主题、语言、登录用户等低频全局数据。
- 缺点是 context value 变化会让消费组件重新渲染。
- 高频状态建议拆分 context,或使用状态管理库、selector 方案。
useReducer 适合什么场景?
- 状态结构复杂,或者更新逻辑依赖 action 类型时适合。
- 它能把状态更新逻辑集中到 reducer 中。
- 表单、多步骤流程、复杂筛选条件都可以考虑
useReducer。
useImperativeHandle 有什么用?
- 它可以自定义父组件通过 ref 能访问的方法。
- 常用于封装输入框、弹窗、播放器等需要暴露命令式能力的组件。
- 不要滥用,能用 props 控制就优先用声明式 props。
useId 用来做什么?
useId用来生成稳定唯一 ID。- 常用于 label 和 input 的可访问性关联。
- 它能避免服务端渲染和客户端 hydrate 时 ID 不一致。
自定义 Hook 的本质是什么?
- 自定义 Hook 是复用状态逻辑的函数。
- 命名必须以
use开头,这样 lint 和 React 规则才能识别。 - 自定义 Hook 复用的是逻辑,不复用状态本身;每次调用都有独立状态。
useEffect 常见错误有哪些?
- 把可以直接计算的数据放进 Effect 再 setState,导致多一次渲染。
- 依赖数组漏依赖,造成闭包读取旧值。
- 在 Effect 中请求数据但不处理竞态,快速切换参数时可能展示旧结果。
如何处理 Effect 请求竞态?
- 可以用
AbortController在清理函数中取消旧请求。 - 也可以用请求序号,只接收最后一次请求结果。
- 使用 TanStack Query 这类库时,很多缓存和竞态问题会被封装掉。
什么是闭包陷阱?
- 函数组件每次渲染都会创建新的作用域。
- 异步回调、定时器、事件监听可能读到当次渲染时的旧 state。
- 可以用依赖数组、函数式更新、ref 或
useEffectEvent处理。
React 19 的 Actions 是什么?
- Actions 是 React 19 中处理异步提交的一套模式。
- 它可以把 pending、错误、乐观更新等状态和提交动作绑定起来。
- 常见场景是表单提交、服务端函数调用、异步更新 UI。
useActionState 有什么用?
useActionState用来根据 Action 的返回值更新组件状态。- 它通常用于表单提交,可以拿到上一次提交结果和 pending 状态。
- 比自己手写 loading、error、result 状态更集中。
useFormStatus 有什么用?
useFormStatus可以读取当前表单的提交状态。- 常见用法是在提交按钮里拿到
pending,从而禁用按钮或展示 loading。 - 它需要在 form 的子组件中使用。
useOptimistic 有什么用?
useOptimistic用于乐观更新。- 请求还没完成时先更新 UI,让交互更快。
- 如果请求失败,再根据真实结果回滚或展示错误。
React 19 中 ref 有什么变化?
- React 19 支持函数组件把
ref当作普通 prop 接收。 - 很多场景不再需要
forwardRef。 - 但老项目和库兼容时,仍可能继续看到
forwardRef写法。
React Server Components 是什么?
- Server Components 是在服务端执行的 React 组件。
- 它不会把组件代码发送到浏览器,因此可以减少客户端 JS。
- 它适合读取数据库、文件系统、内部服务等服务端资源。
Server Components 和 SSR 有什么区别?
- SSR 是一种把组件渲染成 HTML 的渲染方式。
- Server Components 是一种组件模型,决定组件运行在服务端还是客户端。
- 两者可以结合使用,但概念不同。
Server Components 能使用 useState 吗?
- 不能。
- Server Components 不在浏览器中运行,也不持有客户端交互状态。
- 需要交互时,要把那部分拆成 Client Component。
“use client” 是什么?
"use client"标记当前模块进入客户端组件边界。- 该模块及其依赖会被打包到客户端。
- 使用 state、effect、浏览器 API、事件处理时,需要客户端组件。
“use server” 是什么?
"use server"用来声明可被客户端调用的服务端函数。- 它不是 Server Component 的标记。
- Server Function 的参数和返回值需要可序列化,并且要注意权限校验。
Client Component 可以导入 Server Component 吗?
- 通常不能直接导入并执行 Server Component。
- 可以通过 props 的 children 等方式组合,让服务端先渲染好再传入。
- 核心原则是不要把服务端代码拉进客户端 bundle。
Server Components 适合什么场景?
- 适合数据读取、静态内容、权限内聚、减少客户端包体积。
- 不适合点击、输入、拖拽等浏览器交互。
- 实际项目中常见做法是服务端组件负责数据和布局,客户端组件负责交互。
React 19.2 的 Activity 有什么用?
<Activity />可以把 UI 分成可控制优先级的活动区域。hidden模式会隐藏内容、卸载副作用、保留状态,并把更新延后。- 适合预渲染用户可能访问的页面,或保留离开页面的输入状态。
useEffectEvent 解决什么问题?
- 它可以把 Effect 中的“事件逻辑”拆出来。
- 拆出来的函数能读到最新 props 和 state,但不需要放进依赖数组。
- 适合订阅连接成功、定时器 tick、事件监听回调这类场景。
useEffectEvent 和 useCallback 有什么区别?
useCallback是稳定函数引用,依赖变化时会重新生成。useEffectEvent是给 Effect 内部事件使用,能读取最新值但不触发 Effect 重跑。- 不能把
useEffectEvent当作普通事件处理函数到处传。
React 19.2 的 cacheSignal 是什么?
cacheSignal用在 React Server Components 中。- 它可以感知
cache()生命周期结束,从而中止不再需要的异步任务。 - 常用于服务端缓存和请求取消相关场景。
React 19.2 的 Performance Tracks 是什么?
- Performance Tracks 会在浏览器性能面板中展示 React 相关性能轨迹。
- 它帮助定位渲染、更新、Suspense 等性能问题。
- 面试中可以说它提升了 React 应用性能分析的可观测性。
React Compiler 是什么?
- React Compiler 是构建时优化工具。
- 它理解 React 规则,可以自动做 memoization,减少手写
memo、useMemo、useCallback。 - 它主要优化更新性能,不是用来替代所有业务层缓存。
React Compiler 能完全替代 useMemo 吗?
- 不能说完全替代。
- 新代码可以更多依赖 Compiler 自动优化。
- 但需要精确控制引用稳定性、Effect 依赖或跨组件共享缓存时,仍可手写
useMemo/useCallback。
React Compiler 的 “use memo” 是什么?
"use memo"是 React Compiler 的指令。- 在 annotation 模式下,它可以显式标记函数需要被编译优化。
- 一般 infer 模式下不需要到处写,组件命名规范更重要。
React Compiler 的 “use no memo” 是什么?
"use no memo"可以让某个函数跳过 Compiler 优化。- 常用于调试问题或处理暂时不兼容的代码。
- 它是增量接入 Compiler 时的逃生口。
React.memo 有什么用?
React.memo可以在 props 没有变化时跳过组件重新渲染。- 适合渲染成本较高、props 相对稳定的组件。
- 如果 props 每次都是新对象或新函数,memo 效果会变差。
React.memo 为什么不一定提升性能?
- memo 需要比较 props,这本身也有成本。
- 组件很简单时,比较成本可能比重新渲染还高。
- props 经常变化时,memo 几乎没有收益。
如何优化长列表?
- 使用虚拟列表,只渲染可视区域数据。
- 列表项使用稳定 key,避免 index。
- 拆分行组件,配合 memo 和稳定 props。
- 接口层做分页、无限滚动或分片加载。
什么是虚拟列表?
- 虚拟列表只渲染可视区域附近的元素。
- 它通过占位高度模拟完整列表滚动条。
- 适合几千、几万条数据的表格、日志、消息列表。
如何优化首屏性能?
- 路由级代码分割,减少首屏 JS。
- 图片压缩、懒加载、使用合适尺寸。
- SSR / SSG 提前生成 HTML。
- 减少首屏无关依赖和阻塞请求。
Suspense 解决什么问题?
- Suspense 用声明式方式描述异步等待时的 fallback UI。
- 常用于组件懒加载、数据加载、Server Components 流式渲染。
- 它让加载状态从命令式 if 判断变成组件边界。
lazy 和 Suspense 如何配合?
React.lazy使用动态import()懒加载组件。Suspense包裹懒加载组件,并提供 fallback。- 常用于路由切割和非首屏组件加载。
useTransition 有什么用?
useTransition可以把某些更新标记为非紧急更新。- 输入框输入、点击反馈等紧急交互会优先响应。
- 常用于搜索过滤、大列表切换、复杂 tab 内容更新。
useDeferredValue 有什么用?
useDeferredValue可以延迟某个值的更新。- 常用于输入框关键字变化后,延迟更新昂贵列表。
- 它和防抖不同,不是固定时间等待,而是让 React 根据渲染压力调度。
如何减少子组件无意义渲染?
- 合理拆分组件,把变化范围缩小。
- 使用稳定 props,避免每次 render 创建新对象。
- 必要时使用
React.memo、useMemo、useCallback。 - 使用 Profiler 先定位,不要盲目优化。
如何判断 React 性能问题出在哪里?
- 用 React DevTools Profiler 看哪些组件频繁渲染。
- 用浏览器 Performance 看脚本、渲染、布局、网络耗时。
- 分清是渲染慢、请求慢、资源大,还是主线程被阻塞。
Redux 的核心原则是什么?
- 单一数据源:全局状态集中在 store。
- 状态只读:通过 dispatch action 描述变化。
- 纯函数更新:reducer 根据旧 state 和 action 返回新 state。
Redux Toolkit 解决了什么问题?
- 减少传统 Redux 的模板代码。
createSlice把 action 和 reducer 写在一起。configureStore内置常用中间件和开发体验。
Redux、Zustand、Context 怎么选?
- Context 适合低频全局数据,比如主题、语言、用户信息。
- Zustand 更轻量,适合中小型项目或局部全局状态。
- Redux Toolkit 更适合大型团队、复杂状态流、强约束项目。
什么是服务端状态?
- 服务端状态指来自接口、数据库或远端缓存的数据。
- 它需要处理缓存、刷新、失效、重试、并发和加载状态。
- 常用 TanStack Query、SWR 或框架数据层管理。
为什么不建议把接口数据都放 Redux?
- 接口数据有缓存、过期、重试、分页等服务端状态问题。
- Redux 更适合客户端业务状态,不天然解决请求生命周期。
- 用 Query 类库管理服务端状态通常更省心。
React Query / TanStack Query 解决什么问题?
- 管理接口请求、缓存、刷新、重试、分页和乐观更新。
- 减少手写 loading、error、data、refetch 状态。
- 适合把服务端状态从 UI 状态中拆出来。
React Router v6 的特点是什么?
- 使用
Routes和element定义路由。 - 支持嵌套路由和
Outlet。 - 路由匹配规则更清晰,不再依赖 v5 的
Switch。
嵌套路由怎么实现?
- 父路由组件中放
<Outlet />。 - 子路由配置在父路由 children 中。
- 适合后台系统 layout、详情页 tab、分模块页面。
前端权限一般怎么设计?
- 登录态:判断用户是否登录。
- 路由权限:控制页面能否访问。
- 菜单权限:控制导航是否展示。
- 按钮权限:控制操作入口。
- 接口权限:真正安全必须由后端校验。
如何设计一个 Modal 组件?
- 支持受控的
open和onClose。 - 使用 Portal 渲染到 body 下,避免层级和 overflow 问题。
- 支持 Esc 关闭、遮罩关闭、滚动锁定、焦点管理。
- 注意 aria 属性,保证可访问性。
如何设计一个表单组件?
- 要考虑字段值、校验、错误信息、提交状态、默认值和重置。
- 复杂表单可以使用 React Hook Form、Formik 或业务表单引擎。
- 服务端错误要能回显到具体字段或全局提示。
如何封装一个业务组件?
- 先区分通用能力和业务规则,不要过早抽象。
- props 设计要稳定,避免暴露过多内部细节。
- 复杂组件要提供受控和非受控两种使用方式。
- 文档、示例和边界状态比组件代码本身同样重要。
什么是 props drilling?
- props drilling 是为了把数据传给深层组件而层层透传 props。
- 它会让中间组件承担不属于自己的参数。
- 可以用组合模式、Context、状态管理或调整组件结构解决。
组合模式如何减少 props drilling?
- 父组件可以直接把子组件作为 children 或插槽传入。
- 中间组件只负责布局,不需要知道深层组件需要什么数据。
- 这种方式比滥用全局状态更轻。
Error Boundary 有什么用?
- Error Boundary 可以捕获子组件渲染阶段错误。
- 它能展示兜底 UI,避免整个页面白屏。
- 常用于路由级、模块级、组件库级边界。
Error Boundary 不能捕获哪些错误?
- 不能直接捕获事件处理函数里的错误。
- 不能直接捕获异步回调里的错误。
- 不能捕获服务端渲染中的所有错误,需要框架配合。
React 项目如何做代码分割?
- 路由级使用动态
import()。 - 大型第三方库按需加载。
- 非首屏组件延迟加载。
- 分析 bundle,避免把所有依赖塞进主包。
React 项目如何做测试?
- 单元测试覆盖工具函数和核心逻辑。
- 组件测试关注用户行为,不要过度测试实现细节。
- E2E 测试覆盖登录、下单、提交等核心链路。
- 常见工具有 Vitest、Jest、Testing Library、Playwright。
React 中如何做可访问性?
- 表单 label 和 input 要正确关联。
- 弹窗要处理焦点、Esc、aria 属性。
- 按钮用 button,不要用 div 假装按钮。
- 图片提供合理 alt,图标按钮提供可读标签。
React 中如何处理样式?
- 小项目可以用 CSS Modules、Sass。
- 组件库常用 CSS-in-JS、Tailwind 或样式变量。
- 团队要统一方案,避免全局样式污染。
- 设计系统项目要关注 token、主题和暗色模式。
React 中如何处理国际化?
- 文案不要硬编码在组件逻辑中。
- 使用 key 管理多语言资源。
- 注意日期、数字、货币、复数和时区。
- 路由、SEO、服务端渲染也要考虑语言环境。
React 中如何避免内存泄漏?
- 清理定时器、事件监听、订阅。
- 请求组件卸载后不要继续 setState。
- 第三方实例要在清理函数中 destroy。
- 长列表、图表、编辑器尤其要关注释放资源。
React 中为什么不要在 render 中写副作用?
- render 应该是纯计算,输入相同输出相同。
- 副作用放 render 中可能被重复执行。
- 请求、订阅、手动 DOM 操作应该放 Effect 或事件处理函数中。
React 中为什么不要滥用 useEffect?
- 很多派生数据可以直接在 render 中计算。
- 滥用 Effect 容易导致多一次渲染、依赖混乱和状态不同步。
- Effect 应该用于和外部系统同步,而不是普通数据转换。
如何理解组件纯函数?
- 给定相同 props、state、context,组件应返回相同 UI。
- 不应该在渲染过程中修改外部变量或触发请求。
- 纯组件更容易测试,也更适合 React 调度和优化。
如何设计组件的 state?
- 能从 props 或其他 state 推导出的值,不要重复存 state。
- 多个 state 同时变化且强相关,可以合并管理。
- 状态尽量放在真正需要它的最近公共父组件。
什么是状态提升?
- 多个组件需要共享状态时,把状态放到它们最近的公共父组件。
- 父组件通过 props 下发数据,通过回调接收子组件事件。
- 状态提升是解决兄弟组件通信的基础方式。
什么是组件受控和非受控设计?
- 受控组件由外部传入 value 和 onChange。
- 非受控组件内部自己维护状态,外部只给 defaultValue。
- 通用组件最好根据场景支持其中一种或两种模式。
如何封装一个 useDebounce?
import { useEffect, useState } from 'react' |
如何封装一个 usePrevious?
import { useEffect, useRef } from 'react' |
如何封装一个 useToggle?
import { useCallback, useState } from 'react' |
如何封装一个 useClickOutside?
import { useEffect } from 'react' |
如何封装一个 useInterval?
import { useEffect, useRef } from 'react' |
如何封装一个请求 Hook?
- 至少要处理
loading、data、error、run或自动请求。 - 要处理组件卸载、参数变化和请求竞态。
- 复杂场景建议用 TanStack Query,不要重复造完整请求缓存系统。
如何回答 React 性能优化题?
- 先说定位:Profiler、Performance、Network。
- 再说渲染:拆组件、memo、稳定 props、虚拟列表。
- 再说资源:代码分割、图片优化、缓存、SSR/SSG。
- 最后说不要盲目优化,要用数据验证收益。
如何回答 React 19 新特性?
- 可以从 Actions、表单相关 Hook、乐观更新、ref as prop、RSC 这几块说。
- 如果追问 React 19.2,可以补充 Activity、useEffectEvent、cacheSignal、Performance Tracks。
- 回答时最好结合场景,比如表单提交、隐藏页面保留状态、Effect 闭包问题。
如何回答 Server Components?
- 先说它运行在服务端,可以减少客户端 JS。
- 再说它不能用交互 Hook,需要和 Client Component 组合。
- 最后说
"use client"是客户端边界,"use server"是服务端函数,不要混淆。
如何回答 React Compiler?
- 它是构建时优化工具,自动做 memoization。
- 它能减少手写 memo 相关代码,但不是所有性能问题的万能药。
- 迁移时要增量接入,用工具发现不符合 React 规则的代码。
面试中如何讲项目里的 React 优化实践?
- 不要只背 API,要结合项目问题。
- 示例:表格卡顿用虚拟列表,搜索卡顿用 transition 或 deferred value。
- 示例:首屏慢用路由懒加载、减少包体、图片优化。
- 示例:重复请求和缓存混乱,用 TanStack Query 管理服务端状态。
参考资料
- React 19 官方博客:https://react.dev/blog/2024/12/05/react-19
- React 19.2 官方博客:https://react.dev/blog/2025/10/01/react-19-2
- React Server Components:https://react.dev/reference/rsc/server-components
- React Compiler:https://react.dev/learn/react-compiler
- React Compiler Directives:https://react.dev/reference/react-compiler/directives







