人人都会AI编程

附录 C React 高频面试题与核心考点

更新时间:2026-07-11

本附录梳理了 React 面试中最常见、最能考察开发者深度的核心题目,覆盖基础、原理、Hooks、性能优化、工程化及 React 18/19 新特性。每道题都标注了考点,并给出实用回答思路或关键要点。


1. React 基础与核心思想

Q1:React 是框架还是库?它解决了什么问题?

  • 考点:对 React 定位的理解。
  • 回答思路:React 是用于构建用户界面的 JavaScript 库,专注视图层。它不内置路由、全局状态等能力,需配合生态(React Router、Redux 等)组成完整解决方案。核心解决的问题是:复杂 UI 的高效开发与更新,通过声明式、组件化和虚拟 DOM 实现。

Q2:如何理解 React 的声明式编程?

  • 考点:声明式 vs 命令式的区别,React 设计哲学。
  • 回答要点
  • 声明式描述“UI 应该是什么样子”,而非“如何操作 DOM”。
  • 你只需根据状态(state)返回 JSX,React 负责更新 DOM。
  • 优点:代码可预测、易维护,心智负担低。

Q3:什么是虚拟 DOM?它真的比真实 DOM 快吗?

  • 考点:虚拟 DOM 原理,性能认知深度。
  • 回答要点
  • 虚拟 DOM 是一个 JavaScript 对象,描述 UI 结构。
  • 更新时先生成新虚拟 DOM,再与旧树进行 Diff,算最小变更集,最后批量更新真实 DOM。
  • 并非在任何场景下都比手动操作 DOM 快,但提供了可接受的性能保证,并且使跨平台成为可能(抽象了渲染目标)。真正价值在于:开发体验 + 自动批量更新 + 跨平台。

Q4:React 的单向数据流指什么?有何好处?

  • 考点:数据流设计原则。
  • 回答:数据通过 Props 从父组件流向子组件,子组件不能直接修改父组件的状态,只能通过回调函数通知父组件。好处是数据变化路径清晰、易于调试,避免了双向绑定的数据流混乱。

2. 组件与 JSX

Q5:函数组件和类组件有什么区别?为什么推荐函数组件?

  • 考点:组件发展史,Hooks 优势。
  • 核心对比

| 维度 | 类组件 | 函数组件 |
|------|--------|----------|
| 写法 | class 继承,render 方法 | 纯函数,返回 JSX |
| 状态管理 | this.statethis.setState | useState / useReducer 等 Hooks |
| 生命周期 | 完整生命周期方法 | useEffect / useLayoutEffect 等 |
| 代码复用 | HOC / Render Props | 自定义 Hooks(更简单) |
| this 问题 | 需绑定 this | 无 this |
| 打包体积 | 稍大 | 更小 |

  • 推荐函数组件的原因:借助 Hooks 能写出更简洁、可复用的逻辑;没有 class 的各种概念(this、生命周期碎片化);配合 TypeScript 更友好;React 未来演进也以函数组件为主。

Q6:JSX 的本质是什么?为什么 React 必须引入 React?

  • 考点:JSX 编译原理。
  • 回答:JSX 是 React.createElement语法糖,会被 Babel 编译成相应的函数调用。早期版本中 JSX 编译结果直接使用 React.createElement,因此文件里必须引入 React;从 React 17 起引入新的 JSX 转换,不再需要手动引入 React,但 JSX 仍被编译为 _jsx 等函数。

Q7:组件之间如何通信?列举常用方案及适用场景。

  • 回答
  • 父 → 子:Props
  • 子 → 父:回调函数
  • 跨层级:Context API(适合主题、认证信息等全局数据)
  • 兄弟组件:状态提升到公共父组件
  • 复杂应用:全局状态管理库(Redux / Zustand)
  • 偶然发布订阅:Event Bus(不推荐,会破坏数据流可预测性)

3. 状态管理与数据流

Q8:state 和 props 的区别?

  • 考点:基础概念。
  • 回答:State 是组件内部管理的数据,可读写(通过 setState / useState)。Props 是父组件传递给子组件的只读数据。State 类似“组件的记忆”,Props 类似函数的参数。

Q9:setState / useState 的更新是同步还是异步?React 18 有什么变化?

  • 考点:批量更新机制、React 18 自动批处理。
  • 回答
  • 在 React 合成事件和生命周期中,状态更新是异步批量处理的,多次 setState 会被合并,减少渲染次数。
  • 在原生异步(setTimeout、Promise)、原生事件中,React 16/17 默认不批处理,每次更新都可能触发重渲染。React 18 引入了自动批处理,所有更新默认批量(包括异步回调),统一了更新行为,提升了性能。

Q10:Context 的使用场景和性能陷阱是什么?如何优化?

  • 考点:Context 原理与优化。
  • 回答
  • 适合跨层级且变化不频繁的数据,如主题、语言、用户信息。
  • 陷阱:Context 值变化会导致所有消费组件及其子树都重渲染,即使只用到部分数据。
  • 优化:
  • 拆分 Context(不同数据分到不同 Context)。
  • 使用 useMemo 包装 Context value,避免无谓的新对象。
  • 子组件用 React.memouseMemo 防止未变数据下的重渲染。

4. Hooks 核心与原理

Q11:为什么会有 Hooks?它们解决了类组件的哪些问题?

  • 考点:Hooks 设计动机。
  • 回答
  • 难以在组件间复用状态逻辑(HOC/Render Props 易致“嵌套地狱”)。
  • 生命周期方法中包含不相关逻辑(如 componentDidMount 里又拉数据又设订阅),而相关逻辑拆分到不同方法。
  • class 的 this 绑定复杂,编译后体积大。
  • Hooks 允许在函数内部组织逻辑,按功能拆分(自定义 Hooks),更清晰。

Q12:useEffect 和 useLayoutEffect 有什么区别?

  • 考点:执行时机差异。
  • 回答
  • useEffect:在浏览器绘制后异步执行,不阻塞屏幕更新。适合数据请求、订阅、手动修改 DOM(若不影响布局)。
  • useLayoutEffect:在 DOM 更新后、浏览器绘制前同步执行。适合需要读取/同步布局信息的 DOM 操作,防止页面闪烁。尽可能用 useEffect,避免阻塞渲染。

Q13:useMemo 和 useCallback 的原理和适用场景?是否滥用?

  • 考点:缓存机制与性能优化判断。
  • 回答
  • useMemo 缓存计算结果,依赖不变返回缓存值;useCallback 缓存函数引用。
  • 适用场景:传给子组件的引用相等性优化,配合 React.memo 避免不必要重渲染,或计算开销很大的值。
  • 不要无脑使用:缓存本身有开销,对于简单计算或未涉及引用比较的场景是负优化。优化要基于实际测量(React Profiler)。

Q14:useRef 除了获取 DOM 还能做什么?

  • 考点:useRef 的本质。
  • 回答:useRef 返回一个可变的 ref 对象,其 current 属性在组件整个生命周期内保持不变,且改变不会触发重渲染。常见用途:
  • 保存 DOM 节点引用
  • 存储任意可变值(如定时器 ID、上一轮状态),跨渲染周期保持。

Q15:Hooks 的使用规则是什么?为什么?

  • 考点:Hooks 底层存储原理。
  • 回答
  • 规则:只在最顶层调用 Hooks,不要在循环、条件或嵌套函数中调用。只在React 函数组件或自定义 Hook 中调用。
  • 原因:React 依靠 Hooks 的调用顺序来关联状态与 Hook,内部通过链表存储。若顺序改变会导致状态映射错乱。

Q16:如何理解 Hooks 的闭包陷阱?举例说明。

  • 考点:闭包与过期状态。
  • 回答:由于函数组件每次渲染都会重新执行,内部函数可能捕获旧的 state/props 值。若在 useEffect、定时器等异步操作中直接引用 state,可能拿到的不是最新值。解决办法:使用 useRef 保存最新值,或用函数式更新 setState(prev => prev + 1),或正确设置依赖项。

5. 组件复用与高级模式

Q17:高阶组件(HOC)和自定义 Hook 的区别?现代 React 为什么推荐自定义 Hook?

  • 回答
  • HOC 是一个函数,接收组件返回新组件,用于逻辑复用(如 withRouter)。
  • 自定义 Hook 是以 use 开头的函数,内部可使用 Hooks 封装状态逻辑。
  • Hook 更符合函数式思维,不会增加组件层级(避免“嵌套地狱”),且逻辑组合灵活。现代 React 中,几乎所有的逻辑复用优先使用自定义 Hook

Q18:受控组件和非受控组件的区别?什么时候用哪个?

  • 回答
  • 受控组件:表单数据由 React state 驱动,通过 valueonChange 控制。优点是数据源唯一,便于实时验证;缺点是需要写更多回调。
  • 非受控组件:使用 ref 从 DOM 获取表单值,数据由 DOM 自身维护。适合与第三方非 React 代码集成,或简单表单不需要实时验证的场景。
  • 大多数情况推荐受控组件,复杂表单性能问题可通过 React Hook Form 等库解决。

6. 渲染原理与性能优化

Q19:React 的 Diff 算法原理是什么?为什么 key 很重要?

  • 考点:协调过程。
  • 回答
  • 核心策略:
  • 只对同层级节点进行比较,跨层级移动直接销毁重建。
  • 节点类型不同直接重建整个子树。
  • 列表比较使用双端对比算法,配合 key 识别移动与重用。
  • key 的作用:帮助 React 识别哪些元素改变了,当 key 不变时可复用 DOM 和组件实例。错误用法(如用数组索引)在列表顺序变化时会导致状态错乱、性能下降。

Q20:React.memo 如何实现?它和 PureComponent 的区别?

  • 回答:两者都是浅比较 Props 防止不必要渲染。
  • React.memo 是用于函数组件的高阶组件PureComponent 是用于类组件的基类。
  • 均可通过传入自定义比较函数作为第二个参数来控制比较逻辑。

Q21:Fiber 架构解决了什么问题?简单描述渲染阶段。

  • 考点:Fiber 设计思想。
  • 回答
  • 问题:旧 Stack Reconciler 是同步递归的,一旦开始渲染无法中断,复杂组件树会导致主线程长时间占用,造成 UI 卡顿。
  • Fiber 将渲染工作分割成小的工作单元,利用浏览器的空闲时间(requestIdleCallback 思想)可中断恢复。整个渲染分为 Render 阶段(可中断)Commit 阶段(不可中断),Commit 阶段应用变更到 DOM。

Q22:React 18 的并发渲染是什么?如何启用?

  • 考点:Concurrent Mode。
  • 回答:并发渲染允许 React 在渲染过程中暂停并让出主线程去处理紧急更新(如用户输入),从而保持界面响应。你只需使用 createRoot 替代 ReactDOM.render,并使用如 useTransitionuseDeferredValue 等 API 来标记非紧急更新即可获得并发特性,无需特殊开启“并发模式”。

7. 状态管理方案对比

Q23:Redux、Zustand、Jotai 各自的适用场景?

  • 回答
  • Redux:单向数据流严格,生态庞大(中间件、RTK、RTK Query),适合大型团队、需要严格规范和可预测性的项目。
  • Zustand:极简 API(基于 Hook),无 Provider 包裹,学习成本低,适合中小型项目或对模板代码厌倦的团队。
  • Jotai:原子化状态管理,派生和异步自然,适合需要细粒度订阅避免不必要渲染的场景,或全局状态分支多的应用。

Q24:React Query / TanStack Query 解决了什么问题?

  • 回答:它本质是服务端状态缓存库,解决以下痛点:
  • 自动缓存、后台更新、去重请求
  • 简化 loading / error / 数据刷新状态管理
  • 支持乐观更新、分页、无限加载
  • 将服务端状态与客户端 UI 状态分离,避免重复实现请求状态机。

8. React Router 与路由

Q25:React Router v6 的核心变化有哪些?

  • 回答
  • 路由配置改为 <Routes> + <Route element>,组件用 element 属性。
  • 移除 <Switch>,改用 <Routes>
  • 使用 useNavigate 替代 useHistory
  • 支持嵌套路由的更优雅写法(<Outlet>)。
  • 新增数据路由(Loader / Action 进行数据预加载)。

Q26:如何实现路由守卫(权限控制)?

  • 回答:封装一个权限检查组件,包裹需要保护的 <Route>。例如:
  function ProtectedRoute({ children }) {
    const { user } = useAuth();
    if (!user) return <Navigate to="/login" />;
    return children;
  }
  // 使用
  <Route path="/dashboard" element={<ProtectedRoute><Dashboard /></ProtectedRoute>} />
  

9. 测试与工程化

Q27:React 组件的测试策略是什么?

  • 考点:测试体系。
  • 回答
  • 单元测试:用 Jest + React Testing Library 渲染组件,模拟用户交互,断言 DOM 输出(不测试实现细节)。
  • 自定义 Hook 测试:使用 renderHook
  • 集成测试:测试多个组件协作的页面级功能。
  • E2E 测试:用 Cypress / Playwright 模拟真实用户操作流程。

Q28:你在项目中如何进行性能优化?

  • 回答:结合 Profiler 定位瓶颈,然后实施:
  • 避免不必要渲染:React.memouseMemouseCallback、状态下放。
  • 列表虚拟化:react-window 处理长列表。
  • 代码分割:React.lazy + Suspense,路由级懒加载。
  • 图片懒加载、资源预加载。
  • 正确使用 key,避免在渲染中创建新对象/函数作为 Props 导致子组件二次渲染。

10. React 18/19 最新特性

Q29:React 18 的 Suspense 新特性是什么?

  • 回答:Suspense 在 18 中支持异步数据获取(配合 Relay 或 React Query 等支持 Suspense 的库),以及流式 SSR。基本用法:用 <Suspense fallback={<Loading />}> 包裹组件,待组件内数据就绪后显示。

Q30:React Server Components 是什么,解决了什么?

  • 回答:RSC 是在服务端运行的 React 组件,输出零客户端 JavaScript 的 UI 片段。优势:
  • 直接访问服务端资源(数据库、文件系统),无需 API 层。
  • 减少客户端打包体积。
  • 与客户端组件混合使用,通过 'use client' 声明提供交互性。

Q31:React 19 中 use() Hook 是什么?

  • 回答:可以用于客户端组件中读取 Promise 或 Context 的 Hook,替代 useContextSuspense 内部的一些模式。它可以在条件语句中调用(打破 Rules of Hooks 限制),让数据读取更灵活。

11. 综合性思维题

Q32:列表项顺序可能改变时,为什么不能用 index 作为 key?

  • 回答:当数组项重新排序时,index 对应关系改变,React 会错误地重用旧的 DOM 和组件实例,可能导致组件状态(如输入框内容)错乱。唯一稳定的 id 才能保证正确关联。

Q33:如何避免 useEffect 中的无限循环?

  • 回答:检查依赖数组:
  • 是否将引用类型(对象、函数)写进依赖,它们在每次渲染时都生成新引用。
  • 是否在 effect 内部修改了依赖的 state。
  • 使用 useCallbackuseMemo 稳定依赖,或使用 functional update 避免依赖。

Q34:假如让你设计一个通用的自定义 Hook,你会遵循哪些原则?

  • 回答
  • use 开头命名。
  • 返回值尽量简单、意义明确。
  • 对外暴露的接口(参数和返回)稳定,内部封装复杂状态逻辑。
  • 处理好边界(空数据、loading/error)。
  • 遵循最小可用原则,不将无关逻辑塞进 Hook。

这些题目覆盖了从基础知识到源码原理、工程实践的多个层次,面试时除了背诵答案,更重要的是能结合自身项目经验进行深入阐述。希望这份考点总结能帮助你构建系统化的 React 知识脉络,自信应对各类面试。