⚛️
React
12 道题目
难度筛选:
基础 Hooks:
•
•
•
进阶 Hooks:
•
•
•
•
•
规则:只能在函数组件顶层调用,不能在循环/条件/嵌套函数中使用。
•
useState — 状态管理•
useEffect — 副作用处理(类似 componentDidMount/Update/Unmount)•
useContext — 跨组件共享状态进阶 Hooks:
•
useReducer — 复杂状态逻辑•
useMemo — 计算结果缓存•
useCallback — 函数引用缓存•
useRef — 获取 DOM 引用 / 保存可变值•
useLayoutEffect — DOM 更新后同步执行规则:只能在函数组件顶层调用,不能在循环/条件/嵌套函数中使用。
查看答案即标记为已答
虚拟 DOM(Virtual DOM)是真实 DOM 的 JS 对象表示。
工作流程:
1. 状态变化时生成新的虚拟 DOM 树
2. 与旧虚拟 DOM 树进行 Diff 比较
3. 计算最小变更集
4. 批量更新真实 DOM
Diff 算法策略(O(n) 复杂度):
1. Tree Diff:只对比同一层级
2. Component Diff:不同类型组件直接替换
3. Element Diff:通过 key 识别节点移动
key 的作用:帮助 Diff 算法识别哪些元素是同一个,减少不必要的重建。
工作流程:
1. 状态变化时生成新的虚拟 DOM 树
2. 与旧虚拟 DOM 树进行 Diff 比较
3. 计算最小变更集
4. 批量更新真实 DOM
Diff 算法策略(O(n) 复杂度):
1. Tree Diff:只对比同一层级
2. Component Diff:不同类型组件直接替换
3. Element Diff:通过 key 识别节点移动
key 的作用:帮助 Diff 算法识别哪些元素是同一个,减少不必要的重建。
查看答案即标记为已答
useEffect:
• 在 DOM 更新后异步执行
• 不阻塞浏览器渲染
• 适用于数据请求、事件监听等
useLayoutEffect:
• 在 DOM 更新后同步执行
• 阻塞浏览器渲染
• 适用于需要读取/修改 DOM 布局的场景
执行顺序:
DOM 更新 → useLayoutEffect → 浏览器绘制 → useEffect
建议:90% 的场景用 useEffect,只有需要同步修改 DOM 时才用 useLayoutEffect。
• 在 DOM 更新后异步执行
• 不阻塞浏览器渲染
• 适用于数据请求、事件监听等
useLayoutEffect:
• 在 DOM 更新后同步执行
• 阻塞浏览器渲染
• 适用于需要读取/修改 DOM 布局的场景
执行顺序:
DOM 更新 → useLayoutEffect → 浏览器绘制 → useEffect
建议:90% 的场景用 useEffect,只有需要同步修改 DOM 时才用 useLayoutEffect。
查看答案即标记为已答
1. Context + useReducer
优点:内置、轻量
缺点:每次更新会导致所有消费者重渲染
2. Redux / Redux Toolkit
优点:生态成熟、中间件丰富、DevTools 强大
缺点:样板代码多、学习成本高
3. Zustand
优点:API 简洁、性能好、体积小
缺点:生态相对较小
4. Jotai / Recoil
优点:原子化状态、按需更新、适合细粒度场景
缺点:心智模型与 Redux 不同
选择建议:小型项目用 Context/Zustand;大型项目用 Redux Toolkit;细粒度状态用 Jotai。
优点:内置、轻量
缺点:每次更新会导致所有消费者重渲染
2. Redux / Redux Toolkit
优点:生态成熟、中间件丰富、DevTools 强大
缺点:样板代码多、学习成本高
3. Zustand
优点:API 简洁、性能好、体积小
缺点:生态相对较小
4. Jotai / Recoil
优点:原子化状态、按需更新、适合细粒度场景
缺点:心智模型与 Redux 不同
选择建议:小型项目用 Context/Zustand;大型项目用 Redux Toolkit;细粒度状态用 Jotai。
查看答案即标记为已答
1. 父 → 子:Props 传递
2. 子 → 父:回调函数
3. 跨层级:Context
4. 任意组件:状态管理
Redux、Zustand、Jotai 等全局状态方案
5. Ref 转发
<Child name="value" />2. 子 → 父:回调函数
<Child onAction={handler} />,子组件调用 props.onAction(data)3. 跨层级:Context
createContext + Provider + useContext4. 任意组件:状态管理
Redux、Zustand、Jotai 等全局状态方案
5. Ref 转发
forwardRef + useImperativeHandle 暴露子组件方法 查看答案即标记为已答
1. Concurrent Features(并发特性)
•
•
2. Automatic Batching(自动批处理)
• 所有状态更新自动批处理(包括 Promise、setTimeout 中)
• React 17 中只有 React 事件处理函数中才批处理
3. Suspense 增强
• 支持
• 配合 React Lazy 实现代码分割
4. 新 Root API
•
startTransition — 标记非紧急更新•
useDeferredValue — 延迟更新值2. Automatic Batching(自动批处理)
• 所有状态更新自动批处理(包括 Promise、setTimeout 中)
• React 17 中只有 React 事件处理函数中才批处理
3. Suspense 增强
• 支持
<Suspense> 包裹异步组件• 配合 React Lazy 实现代码分割
4. 新 Root API
createRoot(container).render(<App />) 替代 ReactDOM.render 查看答案即标记为已答
key 的作用:帮助 React Diff 算法识别哪些元素发生了变化,提高更新效率。
为什么不能用 index:
当列表发生插入、删除、排序时,index 会变化但元素本身没变,导致:
1. 不必要的 DOM 操作(性能浪费)
2. 组件状态错乱(如输入框的值和实际数据不匹配)
示例:
列表 [A, B, C],在头部插入 D:
• 用 index:React 认为 index=0 的元素内容从 A 变成了 D,需要更新所有
• 用唯一 id:React 知道只是插入了 D,其他不变
建议:使用数据的唯一标识(如 id)作为 key。
为什么不能用 index:
当列表发生插入、删除、排序时,index 会变化但元素本身没变,导致:
1. 不必要的 DOM 操作(性能浪费)
2. 组件状态错乱(如输入框的值和实际数据不匹配)
示例:
列表 [A, B, C],在头部插入 D:
• 用 index:React 认为 index=0 的元素内容从 A 变成了 D,需要更新所有
• 用唯一 id:React 知道只是插入了 D,其他不变
建议:使用数据的唯一标识(如 id)作为 key。
查看答案即标记为已答
Fiber 是 React 16 重写的协调(Reconciliation)引擎。
解决的问题:
• 旧版 Stack Reconciler 递归遍历虚拟 DOM,不可中断
• 大型组件树更新时长时间占用主线程,导致卡顿掉帧
Fiber 核心思想:
1. 链表结构:每个 Fiber 节点有 child/sibling/return 指针,可随时暂停/恢复遍历
2. 时间切片(Time Slicing):将渲染工作拆分为小单元,每帧预留时间给浏览器
3. 优先级调度:不同更新有不同优先级(用户输入 > 动画 > 数据请求)
4. 双缓冲:current 树(当前屏幕)和 workInProgress 树(正在构建),交替使用
工作流程:
1. Scheduler 调度任务,根据优先级决定执行顺序
2. Render Phase(可中断):遍历 Fiber 树,标记需要变更的节点
3. Commit Phase(不可中断):一次性将变更应用到 DOM
Fiber 节点包含:type、key、props、stateNode、effectTag、lanes 等。
解决的问题:
• 旧版 Stack Reconciler 递归遍历虚拟 DOM,不可中断
• 大型组件树更新时长时间占用主线程,导致卡顿掉帧
Fiber 核心思想:
1. 链表结构:每个 Fiber 节点有 child/sibling/return 指针,可随时暂停/恢复遍历
2. 时间切片(Time Slicing):将渲染工作拆分为小单元,每帧预留时间给浏览器
3. 优先级调度:不同更新有不同优先级(用户输入 > 动画 > 数据请求)
4. 双缓冲:current 树(当前屏幕)和 workInProgress 树(正在构建),交替使用
工作流程:
1. Scheduler 调度任务,根据优先级决定执行顺序
2. Render Phase(可中断):遍历 Fiber 树,标记需要变更的节点
3. Commit Phase(不可中断):一次性将变更应用到 DOM
Fiber 节点包含:type、key、props、stateNode、effectTag、lanes 等。
查看答案即标记为已答
Suspense 让组件在等待异步操作(数据加载、代码分割)时显示 fallback UI。
1. 代码分割(配合 React.lazy):
2. 数据获取(React 18+):
3. 服务端流式渲染(SSR):
原理:子组件 throw Promise,Suspense 捕获后显示 fallback,Promise resolve 后重新渲染。
注意:不可以在 Suspense 外部使用 lazy 组件。
1. 代码分割(配合 React.lazy):
const LazyComp = React.lazy(() => import('./HeavyComp'));
<Suspense fallback={<Spinner />}>
<LazyComp />
</Suspense>2. 数据获取(React 18+):
<Suspense fallback={<Skeleton />}>
<UserProfile /> {/* 内部 await 获取数据 */}
<UserPosts /> {/* 独立加载 */}
</Suspense>3. 服务端流式渲染(SSR):
// server
renderToPipeableStream(<App />)
// 每个 Suspense 边界独立流式传输原理:子组件 throw Promise,Suspense 捕获后显示 fallback,Promise resolve 后重新渲染。
注意:不可以在 Suspense 外部使用 lazy 组件。
查看答案即标记为已答
合成事件:React 对浏览器原生事件的跨浏览器封装,提供统一 API。
特点:
1. 事件委托:所有事件绑定在 root 节点(React 17+)而非 document,减少内存占用
2. 事件池(React 16):合成事件对象会被复用,回调执行后属性被置空
3. React 17+ 取消事件池:事件对象不再复用,可直接异步访问
与原生事件的区别:
•
•
• 执行顺序:原生事件(捕获)→ React 事件 → 原生事件(冒泡)
为什么不用原生事件:
• 跨浏览器兼容
• 统一管理(自动卸载)
• Fiber 调度集成
特点:
1. 事件委托:所有事件绑定在 root 节点(React 17+)而非 document,减少内存占用
2. 事件池(React 16):合成事件对象会被复用,回调执行后属性被置空
3. React 17+ 取消事件池:事件对象不再复用,可直接异步访问
与原生事件的区别:
•
e.stopPropagation() 阻止 React 事件冒泡,不阻止原生•
e.nativeEvent.stopImmediatePropagation() 阻止原生事件• 执行顺序:原生事件(捕获)→ React 事件 → 原生事件(冒泡)
为什么不用原生事件:
• 跨浏览器兼容
• 统一管理(自动卸载)
• Fiber 调度集成
查看答案即标记为已答
| useState | useRef | |
|---|---|---|
| 更新触发重渲染 | ✅ | ❌ |
| 值持久化 | ✅ | ✅ |
| 可变/不可变 | 不可变(必须用 setter) | 可变(直接改 .current) |
| 用途 | UI 状态 | DOM 引用、可变值缓存 |
useRef 典型场景:
1. 获取 DOM 引用:
const inputRef = useRef(null);
useEffect(() => inputRef.current?.focus());
return <input ref={inputRef} />;2. 保存可变值(不触发渲染):
const timerRef = useRef(null);
timerRef.current = setInterval(fn, 1000);
// 清除时:clearInterval(timerRef.current);3. 保存上一次的值:
const prevRef = useRef(value);
useEffect(() => { prevRef.current = value; });原则:影响 UI 的用 useState,不影响 UI 的用 useRef。
查看答案即标记为已答
渲染优化:
1.
2.
3.
4.
代码层面:
5. 代码分割:
6. 虚拟列表:react-window / react-virtualized 处理大数据
7. key 优化:列表使用稳定唯一 key
React 18 并发优化:
8.
9.
10.
分析工具:
• React DevTools Profiler — 查看组件渲染次数和耗时
•
1.
React.memo — 浅比较 props,避免不必要重渲染2.
useMemo — 缓存计算结果3.
useCallback — 缓存函数引用4.
useReducer — 替代多个 useState,减少重渲染范围代码层面:
5. 代码分割:
React.lazy + Suspense,按路由或组件懒加载6. 虚拟列表:react-window / react-virtualized 处理大数据
7. key 优化:列表使用稳定唯一 key
React 18 并发优化:
8.
startTransition — 标记非紧急更新(如搜索过滤)9.
useDeferredValue — 延迟渲染某值10.
useSyncExternalStore — 正确订阅外部数据源分析工具:
• React DevTools Profiler — 查看组件渲染次数和耗时
•
why-did-you-render — 检测不必要的重渲染 查看答案即标记为已答