⚛️ React 面试速答

Hooks 原理 / setState / Fiber / 组件通信 / 性能优化 / 虚拟DOM
通俗易懂 · 面试导向 · 一句话能答上来

① Hooks(最高频)

QReact Hooks 是什么?为什么要有 Hooks?
Hooks:让函数组件拥有状态和生命周期的能力(useState/useEffect 等)。

为什么要有:Class 组件的 this 指向混乱、逻辑复用难(HOC/Render Props 嵌套地狱)、生命周期代码分散。Hooks 把相关逻辑聚合在一起,复用更简单。
QuseEffect 和 useLayoutEffect 的区别?
useEffect:在浏览器绘制之后异步执行。不阻塞画面。适合发请求、订阅、日志。
useLayoutEffect:在DOM 更新后、绘制之前同步执行。适合读取 DOM 布局信息后再修改(避免闪烁)。

99% 用 useEffect,只有需要读取 DOM 尺寸/位置并同步修改时才用 useLayoutEffect。
QuseMemo 和 useCallback 的区别?什么时候用?
useMemo:缓存计算结果(值)。useMemo(() => calc(a,b), [a,b])
useCallback:缓存函数引用useCallback(() => doSth(a), [a])

什么时候用:① 传给 React.memo 包裹的子组件时(props 不变就不重渲染);② 计算量大时用 useMemo。
不要滥用:简单的值和函数不用缓存,缓存本身也有开销。
QuseRef 是干什么的?和 useState 区别?
useRef:存一个不触发重渲染的值,通常用来引用 DOM 元素。

区别:useState 改了会重渲染;useRef 改了不会重渲染。useRef 的 .current 在每次渲染间保持不变。

场景:① ref.current.focus() 操作 DOM;② 存定时器 ID;③ 存不需要在页面上显示的可变值。

② setState 是同步还是异步?(必考)

QsetState 是同步还是异步的?
在 React 管理的事件和生命周期里 → 异步(批处理,多次 setState 合并成一次更新)。
在原生事件/setTimeout/Promise 回调里 → 同步(不批处理,立即更新)。

React 18 改进:Automatic Batching,所有场景都批处理(包括 setTimeout/Promise),统一异步。

类比:setState 像"攒着一起改"——React 事件里你改 3 次只渲染 1 次;setTimeout 里每次都立刻渲染(React 18 前的行为)。

③ Fiber 架构(中高级必问)

Q什么是 Fiber?为什么要用?
Fiber:React 16 引入的可中断/可恢复的协调架构。把大的渲染任务拆成小的工作单元(Fiber 节点),可以暂停去处理高优先级任务(用户输入),然后再恢复。

为什么:React 15 的递归更新不可中断,大列表更新时阻塞主线程 → 卡顿。Fiber 让 React 能"分片"渲染。

类比:React 15 = 一次性搬完所有砖(中途不能停);Fiber = 搬一阵歇一阵,有人叫你先让路你就让。
Q虚拟 DOM 和 Fiber 的关系?
虚拟 DOM:JS 对象描述 UI 结构(不可变)。
Fiber 节点:虚拟 DOM 的升级版,可变,带有 child/sibling/return 指针形成链表结构,支持中断恢复。每次渲染 React 把虚拟 DOM 转成 Fiber 树进行 Diff。

④ 组件通信

场景方案说明
父→子props父组件传属性,子组件接收
子→父回调函数 props父组件传一个函数给子组件,子组件调用它
跨级ContextcreateContext + useContext,适合主题/用户信息
兄弟/全局Zustand / Redux状态管理库
父→子方法useImperativeHandle + ref子组件用 forwardRef + useImperativeHandle 暴露方法

⑤ 性能优化

QReact 怎么减少不必要的重渲染?
1. React.memo:包裹组件,props 不变就不重渲染
2. useMemo:缓存计算结果
3. useCallback:缓存函数引用(传给子组件时有用)
4. 列表用 key:帮 Diff 算法识别元素
5. 状态分层:频繁变化的用 useRef(不触发渲染),需要渲染的用 useState
6. 虚拟列表:react-window / react-virtualized
7. 代码分割:React.lazy + Suspense

⑥ 15 道高频面试速答

1React 18 有什么新特性?
Automatic Batching:所有场景的 setState 都自动批处理;② Concurrent Features:useTransition/useDeferredValue 并发渲染;③ Streaming SSR:流式服务端渲染;④ 新的 createRoot API。
2HOC(高阶组件)是什么?和 Hooks 的区别?
HOC 是一个函数:接收组件返回新组件(withAuth(Component))。用于逻辑复用。缺点:嵌套地狱、ref 丢失、props 来源不清晰。Hooks 更简洁,现在推荐用自定义 Hook 替代 HOC。
3React 的 Diff 算法?
同层比较(不跨层);② 类型不同直接替换(整个子树重建);③ 列表用 key 提高复用率。时间复杂度 O(n)。
4key 的作用?用 index 有什么问题?
key 帮 Diff 识别节点。用 index 的问题:列表中间插入/删除时 index 错位,导致状态串了(输入框内容乱跑)。用唯一 id。
5useEffect 的依赖数组为空、有值、不写分别什么效果?
空数组 []:只在挂载后执行一次(相当于 didMount)。有值 [a,b]:a 或 b 变了才执行。不写:每次渲染都执行(很少用)。
6useEffect 怎么清除副作用?
在回调函数里 return 一个清理函数。组件卸载时或依赖变化重新执行前会调用它。常用于清定时器、取消订阅、取消请求。
7Context 怎么用?有什么缺点?
用法:createContext 创建 → Provider 包裹提供值 → 子组件用 useContext 消费。缺点:Context 值变化时所有消费者都重渲染,不适合高频变化的数据(用 Zustand 代替)。
8React.memo 是什么?和 useMemo 区别?
React.memo 包裹组件,props 不变就不重渲染。useMemo 缓存。配合使用:父组件用 useMemo 缓存传给子组件的对象/函数 + 子组件用 React.memo 包裹,才能避免不必要渲染。
9React 事件和原生事件有什么区别?
React 用合成事件(SyntheticEvent),统一了浏览器差异。React 17 前事件绑在 document 上(事件委托),17+ 绑在 root 上。事件对象会被池化复用(17 前异步访问要调 e.persist())。
10什么是错误边界(Error Boundary)?
用 class 组件的 getDerivedStateFromError + componentDidCatch 捕获子组件渲染错误,显示降级 UI,防止整个应用白屏。函数组件不能用,需要用 class 或 react-error-boundary 库。
11forwardRef 是干什么的?
让函数组件能接收 ref 并转发给内部 DOM 元素或子组件。因为函数组件默认没有 ref 属性(不像 class 组件有实例)。配合 useImperativeHandle 可以只暴露特定方法。
12React 怎么做表单?受控和非受控组件?
受控:表单值绑定 state,onChange 更新 state(推荐)。非受控:用 ref 直接读 DOM 值。复杂表单推荐 React Hook Form 库(性能好、验证强)。
13React 的 Concurrent Mode 有什么用?
让渲染可中断:高优先级更新(用户输入)能打断低优先级更新(大数据渲染)。useTransition 标记低优先级,useDeferredValue 延迟值更新。用户输入不卡顿。
14Redux 和 Zustand 怎么选?
大项目(50+页面、多团队)→ Redux Toolkit(DevTools 强、中间件生态)。中小项目 → Zustand(1KB、API 极简、不用 Provider)。2024 趋势:React Query(服务端数据)+ Zustand(客户端状态)组合替代 Redux。
15React Server Components(RSC)是什么?
React 18 + Next.js 13 App Router 引入。组件默认在服务端执行,不打包到客户端 JS,直接访问数据库。减少客户端 bundle 体积。需要 'use client' 指令标记客户端组件。
速记口诀:Hooks替代HOC / useEffect异步useLayoutEffect同步 / setState批处理 / Fiber可中断 / memo+useMemo+useCallback三件套 / key用唯一id / Context慎用(全量重渲染)