React 的调和算法 (Reconciliation) 是如何工作的?
React 的调和算法(Reconciliation)是 React 用来比较两棵虚拟 DOM 树(Virtual DOM Tree),并高效地更新真实 DOM 的核心机制。它的目标是尽可能减少昂贵的真实 DOM 操作,从而提升渲染性能。
以下是 React 调和算法的工作原理:
🧠 一、什么是调和(Reconciliation)
在 React 中,当组件的状态(state)或属性(props)发生变化时,React 会生成一个新的虚拟 DOM 树,并与上一次渲染的旧虚拟 DOM 树进行比较。这个过程叫做调和(Reconciliation)。
它通过对比新旧虚拟 DOM,找出最小变更集,然后批量应用到真实 DOM,这个步骤称为 Diffing。
⚙️ 二、调和算法的核心假设与策略
React 使用了一些高效 diffing 的前提假设来优化性能:
不同类型的元素会产生不同的树结构:
- 如果根元素的类型不同(如从
<div>变成<span>),React会直接销毁旧子树并重建新的子树。
- 如果根元素的类型不同(如从
通过
keyprop来标识列表中的稳定子节点:- key帮助 React识别哪些元素改变了位置、被添加或被删除。没有key或使用不稳定的key会导致性能下降或bug。
基于这些假设,React使用如下策略进行diff:
🔍 三、Diffing过程详解
✅ Step1: Element Type不同
- React认为整个子树需要重新构建。
- old tree会被unmounted(触发componentWillUnmount等生命周期)。
- new tree会被完全创建和mounted(触发constructor, componentDidMount等)。
例如:
// Before
<div>
<Counter />
</div>
// After
<span>
<Counter />
</span>
由于外层标签从 div -> span,React会卸载旧的 <div>及其所有子组件并挂载新的 <span>及其子组件。
✅ Step2: Element Type相同但属性变化
- React会复用该DOM节点,仅更新变化的属性。
- style/class/event handler等都会被智能地更新。
例如:
// Before
<div className="box" title="hello"/>
// After
<div className="container" title="world"/>
只会修改className和title对应的DOM属性值。
✅ Step3: Component Element类型相同(对于类组件或函数组件)
- React会保留组件的实例(如果是类组件),只调用其生命周期方法或重新执行函数逻辑。
- props的改变会触发组件的重新渲染(
render()或函数体再次执行)。
例如:
<MyComponent name="Alice" /> → <MyComponent name="Bob" />
name改变导致 MyComponent re-render。
✅ Step4: Children的协调处理 — List Diffing & Key的作用
这是最复杂的部分。当父元素有多个子节点时:
Without Keys:
默认按顺序比较每一个子节点。一旦顺序变动或有插入/删除操作,可能导致大量不必要的DOM重创建。
With Keys:
Key让React能够识别每个节点的身份是否保持不变。即使位置变了也能做到最小化移动DOM操作。
示例:
<ul>
<li key="a">Apple</li>
<li key="b">Banana</li>
</ul>
→
<ul>
<li key="b">Banana</li> <!-- Moves -->
<li key="a">Apple</li>
</ul>
有key的情况下React知道只是交换了顺序而不是重建两个 li。
🚀 Fiber架构下的调和机制升级 (React16+)
在 React16之后引入了 Fiber Reconciler ——一种可中断、优先级调度友好的新架构:
特点包括:
- 增量渲染:将渲染工作拆分成多个小单元并分布到多帧中完成。
- 任务优先级调度:高优先级的任务如动画可以打断低优先级任务先执行。
- 支持并发模式和新特性(Suspense, Transitions等)。
Fiber把每个 fiber node作为工作单元记录必要信息以实现暂停和恢复工作流的能力。
工作流程分为两个阶段:
Render Phase (可中断):
- Do reconciliation and build the workInProgress tree.
- No actual DOM updates happen here.
Commit Phase (不可中断):
- Apply changes to real DOM synchronously.
- Call lifecycle methods or hooks like useEffect.
📝 Summary: Reconciliation的关键点总结
| Concept | Description |
|---|---|
| Virtual DOM | In-memory representation of UI |
| Diffing Algorithm | Compares old and new VDOM trees |
| Element type change | Triggers full subtree rebuild |
| Same element with different props | Only update changed attributes |
| Lists need keys | To track identity/movement efficiently |
| Fiber architecture | Enables incremental rendering and concurrency |
🔚 React的调和算法是其高性能UI更新的关键所在。理解其工作原理有助于我们写出更高效的组件结构和合理使用key等最佳实践。