前端 React 技术栈历经多次更新,面试中的考点也在不断演变。以下整理了一些近年来面试中常被问到的 React 问题,以及对应的标准答案思路与示例。建议你除了会背答案外,更要理解底层原理与实战场景。
基础与核心原理
问题 1:React 是什么?它有哪些核心特性?
答案思路:
React 是一个用于构建用户界面的 JavaScript 库,主要用于 SPA(单页应用)。它的核心特性包括:虚拟 DOM、组件化、单向数据流、声明式 UI、可组合性(组件复用),以及支持生命周期或 Hooks 来处理副作用与状态。
问题 2:什么是 JSX?它如何工作?
答案思路:
JSX 是 JavaScript 的语法扩展,看起来像 HTML,但本质上在构建时(如通过 Babel)被转换成 React.createElement(...) 调用。浏览器不能直接理解 JSX。JSX 能让定义 UI 结构更直观,并支持在中括号 {} 中嵌入表达式。
问题 3:虚拟 DOM(Virtual DOM)与实际 DOM,有什么区别?
答案思路:
虚拟 DOM 是 React 在内存中的一个轻量化表示。当状态或属性改变时,React 会创建新的虚拟 DOM 树并与旧的树进行 diff 比较,只把变化的部分更新到实际 DOM 中。这样能减少不必要的 DOM 操作,提高性能。
组件与状态管理
问题 4:什么是函数式组件(Functional Component)与类组件(Class Component)的区别?
答案思路:
类组件可用生命周期方法(如 componentDidMount, componentDidUpdate 等),定义状态 state 在 this.state 中,写法较冗长。
函数式组件在 React Hooks 出现以后可以管理状态(useState)、处理副作用(useEffect)等;写法简洁、易于复用。现代推荐使用函数式组件。
问题 5:props 与 state 的区别是什么?
答案思路:
props 从父组件传入子组件,用来配置组件或传数据,子组件不能直接修改 props,是只读的。
state 是组件内部管理的可变数据,用来反映组件状态的变化,引起重新渲染。函数式组件通常用 useState 或 useReducer 来管理状态。
问题 6:什么是受控组件(controlled component)和非受控组件(uncontrolled component)?
答案思路:
受控组件中表单元素的值由 React state 控制,每次用户输入触发事件更新 state,这样 React 完全掌握表单状态。
非受控组件让 DOM 本身管理值,通常通过 ref 获取当前值,适合简单表单或不需要频繁响应输入变化的场景。
Hooks 与副作用
问题 7:useEffect 的作用是什么?依赖数组(dependency array)如何影响 useEffect?
答案思路:
useEffect 用于在组件渲染后执行副作用操作,如数据获取、订阅、DOM 操作等。依赖数组控制副作用什么时候运行/清除/重新运行:
- 如果依赖数组为空,就仅在组件挂载完成后执行一次;
- 如果包含某些依赖变量,只有这些变量变更时才重新执行;
- 不指定数组每次渲染都会触发。
问题 8:useLayoutEffect 与 useEffect 的区别?什么时候使用 useLayoutEffect?
答案思路:
useLayoutEffect 与 useEffect 类似,但触发时间不同。useLayoutEffect 在 DOM 更新渲染之前同步调用,用于测量 DOM 布局、读取布局属性或强制同步变更。useEffect 在浏览器绘制之后运行,对用户体验影响小。一般用于视觉布局调整等特殊场景才用 useLayoutEffect。
问题 9:什么是 useMemo 和 useCallback?它们怎样帮助性能优化?
答案思路:
useMemo 用来缓存某个“重计算”值,只在依赖更新时重新计算,避免每次渲染都做昂贵计算。
useCallback 缓存函数实例,防止子组件因 props 中函数是新引用而频繁重渲染。
合理使用这些 Hook 能减少不必要渲染与计算,但滥用也可能增加复杂度或内存占用。
组件生命周期/性能优化/进阶话题
问题 10:什么是 React 的 Reconciliation?
答案思路:
Reconciliation 是指 React 比较新旧虚拟 DOM 树,确定哪些节点需要变更,然后最小化地更新实际 DOM 的过程。这一过程涉及 diff 算法、Key 的使用等。正确使用 key 以及避免不必要渲染非常关键。
问题 11:什么是错误边界(Error Boundary)?怎样写一个错误边界?
答案思路:
错误边界是一种 React 组件(必须为类组件)用来捕获子组件树在渲染/生命周期/构造期间抛出的 JavaScript 错误,并显示一个备用 UI,而不会让整个应用崩溃。实现方法包括定义 static getDerivedStateFromError(error) 和 componentDidCatch(error, info) 方法。
问题 12:代码分割(Code Splitting)与 React.lazy/Suspense 如何工作?
答案思路:
代码分割是将应用拆分成多个 bundle,在需要时加载某部分 JS 文件,以减少初始加载体积。React.lazy 用于动态 import 组件,Suspense 用于在等待加载期间显示 fallback UI。对于大型应用或路由较多的应用特别重要。
问题 13:什么是 Context?使用 Context 的坑有哪些?
答案思路:
Context 用来在组件树中跨层传递数据,不必逐层传 props,例如主题、用户认证信息等。坑包括:Context 值变化可能导致很多子组件重渲染;过度使用 Context 会使组件耦合;Context 适合大范围共享状态,但不一定替代状态管理库。
实战技巧与面试准备建议
- 除了理解概念,还要能写代码示例,面试中往往要求手写小组件、写 useEffect、useReducer 等。
- 了解 React 最新版本中新增加的特性/API,比如 Concurrent Mode、Suspense 的改进、新的 Hooks(如 useId 等)。
- 能分析一个组件为什么重渲染,如何调试性能瓶颈。
- 实际项目经验很加分,例如你如何做优化、拆分组件、管理状态、处理异步加载等。
- 准备常见题型:props/state 管理、生命周期/Hooks、性能优化、路由、测试(如 react-testing-library/Jest)等。