React 性能优化总结

171 阅读4分钟

1. 理解重新渲染

定义
当组件的状态(state)或属性(props)变更时,会触发重新渲染。默认情况下,当一个组件重新渲染时, React 将递归渲染它的所有子组件。

✅ 核心要点

  • React 对比新旧虚拟 DOM,执行协调过程。
  • 即使输出结果相同,协调过程仍会发生。
  • React 仅在渲染之间存在差异时才会更改 DOM 节点。

⚠️ 注意事项

  • 传递新的对象/数组会触发子组件的重新渲染:
<MyComp data={{x:1}} /> // 每次渲染生成新对象
  • Context 更新会重新渲染所有消费者(consumers)。
  • React.memo 仅在属性浅层比较相等时阻止重新渲染。

🎯 面试一句话总结
重新渲染由状态/属性变更或父组件更新触发。传递新对象引用或 Context 变更往往是性能瓶颈的根源。


2. React.memo, useMemo, useCallback

✅ React.memo

  • 包裹组件,对属性进行浅层比较。
  • 避免不必要的重新渲染。

✅ useMemo

  • 在渲染间缓存值。
  • 用于昂贵计算或派生值。

✅ useCallback

  • 缓存函数,避免创建新函数引用。
  • 适用于向已记忆化的子组件传递回调。

⚠️ 注意事项

  • useMemo/useCallback 本身有成本,仅在必要时使用。
  • 浅层比较无法检测嵌套对象变更。
  • 过度使用可能反而降低性能。

🎯 面试一句话总结
React.memo 通过属性浅比较避免重渲染。useMemo 缓存值,useCallback 缓存函数,滥用可能适得其反。


3. 批量更新(Batching Updates)

定义
React 将多次状态更新合并为单次渲染。

✅ React 18 之前

  • 批量更新仅发生在事件处理函数内。

✅ React 18+

  • 批量更新扩展至所有上下文(定时器、异步回调、Promise)。

示例

setCount(c => c+1);
setValue(v => v+1);
// React 18 → 触发一次渲染

🎯 面试一句话总结
React 18 实现全场景自动批量更新,减少渲染次数。此前仅限事件处理函数内。


4. 并发渲染(Concurrent Rendering)

定义
React 18 的并发模式(默认启用)允许根据优先级中断、暂停或重启渲染。

✅ 核心要点

  • 支持时间切片(time slicing)— 渲染期间保持 UI 响应。
  • 新增 API:useTransition, useDeferredValue。
  • 不改变最终 UI,仅优化 React 调度策略。

⚠️ 注意事项

  • 提交前可能经历多次渲染过程。
  • 副作用需放入提交阶段(用 useEffect,而非渲染函数)。

🎯 面试一句话总结
并发渲染使 React 可按优先级暂停/恢复工作。它不改变 UI 结果,仅优化调度逻辑。


5. useTransition & useDeferredValue

✅ useTransition

  • 拆分更新为紧急与非紧急。
  • 确保重型渲染期间 UI 响应性:
  const [isPending, startTransition] = useTransition();
  startTransition(() => setSearchResults(expensiveCompute(query)));

✅ useDeferredValue

  • 延迟更新值直至空闲时段:
  const deferredQuery = useDeferredValue(query);

🎯 面试一句话总结
useTransition 标记非紧急更新以保持响应;useDeferredValue 推迟高成本渲染至空闲时段。


6. 虚拟化(大型列表)

定义
仅渲染列表中可见项,而非一次性处理全部。
✅ 推荐库

react-window, react-virtualized。

✅ 优势

  • 大幅提升万级列表性能。
  • 显著减少 DOM 节点数量。

⚠️ 注意事项

  • 需精确计算高度(行高/列宽)。
  • 隐藏元素影响可访问性与 SEO。

🎯 面试一句话总结
虚拟列表仅渲染可见列表项,对海量数据渲染优化至关重要,但需妥善处理高度计算与可访问性。


7. Suspense 数据获取

定义
Suspense 允许组件在渲染后备方案前“等待”异步数据。

✅ 核心要点

  • Suspense 边界避免瀑布流请求。
  • 需配合 React 数据框架(如 Next.js, Relay)使用。
  • 支持流式服务端渲染(SSR)。

⚠️ 注意事项

  • 仅处理 Promise 而非任意异步操作。
  • 错误处理需依赖错误边界。

🎯 面试一句话总结
Suspense 使 React 能通过后备方案等待异步数据,简化异步逻辑,但需用错误边界捕获异常。


8. 避免高成本渲染

✅ 优化技术

  • 虚拟化(Virtualization)长列表。
  • 防抖/节流(Debouncing/Throttling)用户输入。
  • 懒加载组件(React.lazy)。
  • 精细化 Context(拆分 Provider)。
  • 选择器钩子(Selector hooks)订阅状态切片。

🎯 面试一句话总结
通过记忆化、列表虚拟、输入防抖、代码懒加载及状态上下文拆分优化渲染性能。

9. 性能调试 (Debugging Performance)

✅ 工具 (Tools)

  • React DevTools 性能分析器(渲染火焰图)
  • why-did-you-render 库(检测非必要渲染)
  • 浏览器性能标签页

✅ 常见问题

  • 渲染风暴(Re-render storms):短时间内密集触发多次渲染
  • 输入延迟(Input lag):用户交互响应卡顿
  • UI 闪烁(Flickering UI):界面元素异常抖动或重绘

🎯 面试一句话总结
通过 React DevTools 和 why-did-you-render 定位渲染风暴;输入延迟或 UI 闪烁通常由无效渲染引起。