返回首页

React UI 更新:render 和 commit 阶段

本文通过 trigger、render 和 commit 阶段分解 React 中的 UI 更新过程。描述了 Fiber 结构、reconciliation、flags 以及与浏览器管道的交互。有助于理解 concurrent mode 中的优化。

React 如何渲染:Fiber、flags、commit
Advertisement 728x90

React UI 更新流程:从触发到提交

React 更新界面遵循严格的顺序:触发 → 渲染 → 提交。在渲染阶段,React 会计算新的虚拟 DOM 树,但不会直接操作真实 DOM。提交阶段则同步应用变更。这种分离机制支持性能优化——尤其是在并发模式下,渲染可依据优先级被中断。

渲染阶段:协调与 Fiber 树

渲染阶段基于当前的 Fiber 结构构建一个正在进行中的工作树。Fiber 是一种链表结构,用于表示组件层级关系。每个节点通过 alternate 属性保存对前一版本的引用,便于进行差异比对。

协调过程将新的 React 元素(来自 JSX)与旧元素进行对比。稳定的 key 值能确保精准匹配,减少不必要的更新。

Google AdInline article slot

此阶段分为两个阶段:beginWork(自上而下)和 completeWork(自下而上):

  • beginWork:判断是否需要重新渲染(若无需则跳过),调用组件的 render 函数,设置标志位(Placement、Update、Deletion),并递归处理子节点。
  • completeWork:在处理完子节点后,完成当前节点的最终处理,并聚合子树的标志位以优化提交阶段。

子树标志位向上传播变更信息,在提交时跳过未变化的分支。

| 标志位 | 说明 |

Google AdInline article slot

|------|------|

| Placement | 插入 DOM 节点 |

| Update | 更新属性或文本内容 |

Google AdInline article slot

| Deletion | 删除元素 |

| Ref | 处理 refs |

| Layout | 调度 useLayoutEffect |

| Passive | 调度 useEffect |

标志位在渲染阶段设定,于提交阶段执行。

提交阶段:DOM 变更

提交阶段是同步且原子性的,分为三个子阶段:

  • beforeMutation:调用 getSnapshotBeforeUpdate(类组件)并清理旧的 refs。
  • mutation:根据标志位执行插入、更新或删除 DOM 节点的操作。
  • layout:同步执行 componentDidMount/UpdateuseLayoutEffect,更新 refs,并调度 useEffect

布局阶段结束后,浏览器接管后续流程:样式重计算 → 布局/重排 → 绘制 → 合成。React 不再干预这些步骤。

提交后的浏览器流水线

useEffect 异步运行,通常在绘制之后;但对于离散事件(如点击、按键、输入),可能在绘制前执行,以保持响应性。

离散事件:

  • click, keydown, input, submit

连续事件(如 scroll、mousemove)始终在绘制之后执行。

并发模式:可中断的渲染

在并发模式下,渲染被拆分为多个优先级任务单元。高优先级更新(如输入字符)可中断低优先级任务(如之前的渲染),此时工作树会被丢弃。

提交阶段保持不变:同步执行,每轮更新仅一次。这确保了 DOM 的一致性。

若更新被跳过,渲染可多次重启而无需提交。

核心要点

  • 渲染阶段在内存中计算变更;只有在提交阶段存在标志位时才会发生 DOM 变更。
  • beginWork/completeWork 通过跳过和子树标志位优化遍历效率。
  • useLayoutEffect 在 mutation 后同步执行;useEffect 在绘制后异步执行。
  • 并发模式允许中断渲染,但提交仍不可分割。
  • 稳定的 key 值对高效协调至关重要。

— Editorial Team

Advertisement 728x90

继续阅读