⭐ 总体结构
触发渲染
↓
🚧 1. Render 阶段(协调阶段 / Reconciliation)
↓
⚡️ 2. Commit 阶段(提交阶段 / DOM 操作)
⭐ 一、触发渲染的三个来源
React 不会无缘无故渲染,一个组件的渲染只会由以下情况触发:
1️⃣ 父组件渲染导致子组件渲染(默认行为)
2️⃣ 组件内部状态更新(setState / useState)
3️⃣ Context 变化导致使用 useContext 的组件更新
⭐ 二、React 渲染过程
🚧 阶段 1:Render 阶段(Reconciliation 协调阶段)
目标:计算出需要更新什么,生成新的 Fiber Tree
这是一个 纯计算阶段:
- 不会操作 DOM
- 可被暂停、可打断(Concurrent Mode)
- 可恢复
Render 阶段会做什么?
- 执行函数组件(执行组件体)
- 运行 Hooks(useState、useMemo 等)
- 比较新旧 Fiber 树(diff)
- 生成更新链表(effect list)
Render阶段产物:
- 一个 需要被提交到 DOM 的更新表
注意:render 不是指"渲染到 DOM",而是"构造新的虚拟 fiber 树"。
⚡️ 阶段 2:Commit 阶段(提交阶段 / 实际 DOM 操作)
Commit 阶段无法被打断,因为涉及到真实 DOM。
分三步:
1️⃣ before mutation(提交前)
执行:
- getSnapshotBeforeUpdate
- 调整内容前的准备
2️⃣ mutation(变更 DOM)
执行所有 DOM 操作:
- 插入 DOM
- 删除 DOM
- 修改 DOM 属性
- ref 的更新
3️⃣ layout(布局阶段)
执行 Layout Effects:
- useLayoutEffect 回调
- class 组件的 componentDidMount / componentDidUpdate
Mutation 和 Layout 这两步是同步执行的,不允许中断。
⭐ React Hook 与两个阶段的关系
| Hook | 执行阶段 |
|---|---|
| useState setter → 触发 Render | 渲染开始前 |
| useMemo / useCallback | Render 阶段 |
| useEffect | Commit - layout 后(异步) |
| useLayoutEffect | Commit - mutation 与 paint 前 |
| ref 更新 | Commit - mutation 阶段 |
两个关键点:
- useEffect 在 Commit 后异步执行,不阻塞渲染
- useLayoutEffect 会阻塞浏览器绘制(类似 componentDidMount)
React 为什么要分 Render 和 Commit?
🧠 Render 阶段可中断=流畅度提升
因为计算可以被暂停,让位给用户输入。
⚡️ Commit 阶段不可中断=保持 DOM 一致性
保证真实 UI 操作的原子性,不会出现中断状态。
⭐ 完整流程图
state 更新 / props 更新 / context 更新
↓
---- Render 阶段(可中断) ----
1. 执行组件函数
2. 运行 hooks
3. 生成新的 Fiber 树
4. 计算 diff
5. 构建 effectList(DOM 变更清单)
↓
---- Commit 阶段(不可中断) ----
1. before mutation(DOM 操作前的准备)
2. mutation(真实 DOM 更新)
3. layout(执行 useLayoutEffect、componentDidMount)
↓
浏览器绘制(paint)
↓
最后执行 useEffect(异步)