01 · NOTES / WebGIS 开发
第 13 节 · 渲染机制
2026 年 9 月 1 日
第 13 节 · 渲染机制
📌 版本信息:React 19.x(2026-08-29 核对) 📚 来源:React 中文文档 · 渲染与提交 | 作为 UI 树
一、这一节的目标
- 精通渲染三步:触发 → 渲染 → 提交
- 理解 Diff(协调)算法的三条假设与 key 的作用(第 05 节原理版)
- 分清"触发重渲染"的三类原因与"渲染 ≠ DOM 更新"
- 建立性能直觉:什么操作让 React 做无用功
二、渲染三步
① 触发(Trigger)
首次渲染:root.render(<App/>)
重渲染:组件的 state 更新(自己的或祖先的)
② 渲染(Render,纯计算,不碰屏幕)
React 递归调用组件函数 → 得到新的 JSX 树(React Element 树)
⚠️ 这一步是"在脑子里比划",你的组件函数在这里被执行!
③ 提交(Commit)
React 与真实 DOM 比对(Diff/协调),只把差异应用到 DOM
然后浏览器绘制 —— useLayoutEffect 在绘制前、useEffect 在绘制后跑
关键认知:
- "渲染"= 调用你的组件函数,不等于操作 DOM——渲染结果与上次相同则 DOM 一步都不动
- 组件函数被频繁调用是常态(它的开销是纯 JS 计算,通常很便宜)——所以第 02 节要求它纯粹
- setState → 调度 → 渲染 → 提交,全程异步批处理(第 03 节)
三、Diff 的三条假设
比对两棵元素树时,React 靠三条假设把 O(n³) 的树比对降到 O(n):
- 不同类型的元素 → 摧毁子树重建:
<div>变<span>,整个子树全部卸载重建(内部 state 丢失!) - 同类型元素 → 保留 DOM 节点,只更新变化的属性
- 同层列表 → 按 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)。
六、自测题
- 渲染(render)与提交(commit)的区别?渲染会碰 DOM 吗?
- Diff 三条假设分别是什么?key 对应哪条?
<div>换成<section>,子树会发生什么?state 呢?- 祖先 setState 为什么会让"props 没变的后代"也重渲染?
- 为什么说"组件函数被频繁调用不是坏事"?
参考答案
- 渲染=调用组件函数得到新元素树(纯计算);提交=把 Diff 差异写入真实 DOM。渲染本身不碰 DOM。
- 异类型销毁重建 / 同类型只更新属性 / 同层列表按 key 配对;key 是第三条。
- div 子树整体卸载重建;其内部 state 全部丢失。
- React 的默认策略是"祖先更新 → 后代全量重渲染"(乐观假设渲染便宜);阻止需要 memo(配合稳定引用)或状态下沉。
- 组件函数是纯 JS 计算(无 DOM 操作),执行成本通常远小于 DOM 更新;React 用"多算少动"换取正确性。
七、下一步
看穿渲染 → 第 14 节:状态管理全景,Context/Zustand/Redux 的选型地图。
TAGSweb