返回笔记列表
01 · NOTES / WebGIS 开发

第 13 节 · 渲染机制

2026 年 9 月 1 日

第 13 节 · 渲染机制

📌 版本信息:React 19.x(2026-08-29 核对) 📚 来源:React 中文文档 · 渲染与提交作为 UI 树

一、这一节的目标

  1. 精通渲染三步:触发 → 渲染 → 提交
  2. 理解 Diff(协调)算法的三条假设与 key 的作用(第 05 节原理版)
  3. 分清"触发重渲染"的三类原因与"渲染 ≠ DOM 更新"
  4. 建立性能直觉:什么操作让 React 做无用功

二、渲染三步

① 触发(Trigger)
   首次渲染:root.render(<App/>)
   重渲染:组件的 state 更新(自己的或祖先的)

② 渲染(Render,纯计算,不碰屏幕)
   React 递归调用组件函数 → 得到新的 JSX 树(React Element 树)
   ⚠️ 这一步是"在脑子里比划",你的组件函数在这里被执行!

③ 提交(Commit)
   React 与真实 DOM 比对(Diff/协调),只把差异应用到 DOM
   然后浏览器绘制 —— useLayoutEffect 在绘制前、useEffect 在绘制后跑

关键认知

  1. "渲染"= 调用你的组件函数,不等于操作 DOM——渲染结果与上次相同则 DOM 一步都不动
  2. 组件函数被频繁调用是常态(它的开销是纯 JS 计算,通常很便宜)——所以第 02 节要求它纯粹
  3. setState → 调度 → 渲染 → 提交,全程异步批处理(第 03 节)

三、Diff 的三条假设

比对两棵元素树时,React 靠三条假设把 O(n³) 的树比对降到 O(n):

  1. 不同类型的元素 → 摧毁子树重建<div><span>,整个子树全部卸载重建(内部 state 丢失!)
  2. 同类型元素 → 保留 DOM 节点,只更新变化的属性
  3. 同层列表 → 按 key 配对(第 05 节 key 的原理根据)
// 假设 1 的实战意义:
{isLogin ? <Dashboard /> : <Login />}     // 切换时 state 全部重置 ✅(通常正是想要的)
{showA && <Panel/>} {showB && <Panel/>}   // 同名组件在不同位置 → 也是"不同树",state 丢失!
// 想让"同一个组件"在两个位置切换时保留 state:给它相同的显式 key

四、什么触发了重渲染

触发源
自己的 state 更新 setCount(...)
祖先的任何 state 更新 祖先 set 了 → 整棵子树默认全量重渲染
Context value 变化 该 Context 的所有消费者

第三条是第 07 节"Context 性能坑"与第 11 节"Zustand 精准订阅"的原理:默认情况下祖先重渲染 = 所有后代重渲染(哪怕 props 没变)——React 乐观地假设组件渲染很便宜,把优化留给需要的组件(React.memo,第 15 节)。


五、性能直觉清单

无用功 后果 解法
渲染中做昂贵计算 每次渲染都白算 useMemo(第 12 节)
大列表全量渲染 提交慢、长页面卡 虚拟列表(react-window,数据大时)
祖先一动全局重渲染 大面积无效渲染 组件拆分 + memo + 状态下沉/上提
在渲染里 new 函数/对象传给 memo 子组件 memo 失效 useCallback(第 12 节)
Effect 依赖不全导致的连环触发 死循环渲染 依赖数组纪律(第 12 节)

💡 判断标准永远是测量优先:React DevTools Profiler 录一段交互,看哪些组件渲染了几次、耗时多少——凭感觉优化是反模式(第 15 节正式学 Profiler)。


六、自测题

  1. 渲染(render)与提交(commit)的区别?渲染会碰 DOM 吗?
  2. Diff 三条假设分别是什么?key 对应哪条?
  3. <div> 换成 <section>,子树会发生什么?state 呢?
  4. 祖先 setState 为什么会让"props 没变的后代"也重渲染?
  5. 为什么说"组件函数被频繁调用不是坏事"?

参考答案

  1. 渲染=调用组件函数得到新元素树(纯计算);提交=把 Diff 差异写入真实 DOM。渲染本身不碰 DOM。
  2. 异类型销毁重建 / 同类型只更新属性 / 同层列表按 key 配对;key 是第三条。
  3. div 子树整体卸载重建;其内部 state 全部丢失。
  4. React 的默认策略是"祖先更新 → 后代全量重渲染"(乐观假设渲染便宜);阻止需要 memo(配合稳定引用)或状态下沉。
  5. 组件函数是纯 JS 计算(无 DOM 操作),执行成本通常远小于 DOM 更新;React 用"多算少动"换取正确性。

七、下一步

看穿渲染 → 第 14 节:状态管理全景,Context/Zustand/Redux 的选型地图。

TAGSweb