【React】diff 算法
React 的 Diff 算法(差异协调算法)是虚拟 DOM 机制的核心,用于高效更新真实 DOM。其核心目标是通过最小化 DOM 操作(如插入、删除、移动)提升性能,时间复杂度从传统算法的 O(n³) 优化至 O(n)。以下从原理、策略、优化到实践展开详解:
⚙️ 一、核心设计原则
-
分层比较(Tree Diff)
- 仅对比同一层级的节点,忽略跨层级移动。
- 原因:跨层级操作实际较少见,避免复杂度飙升。
- 示例:若节点从
<div>
移动到<section>
的更深层级,React 会销毁旧节点并重建新节点,而非移动。
-
组件类型比较(Component Diff)
- 相同类型组件:复用实例,更新
props
并触发生命周期(如componentDidUpdate
)。 - 不同类型组件:销毁旧组件及其子树,创建新组件(触发
componentWillUnmount
和componentDidMount
)。
- 相同类型组件:复用实例,更新
-
元素类型比较(Element Diff)
- 不同类型元素(如
<div>
→<span>
):直接替换整棵子树。 - 相同类型元素:更新属性(如
className
),递归比对子节点。
- 不同类型元素(如
🔍 二、列表对比优化:Key 与双指针策略
1. Key 的作用
-
唯一标识:通过
key
识别节点身份,避免顺序变化时的误判。 -
错误示例:使用索引
key={index}
时,列表头部插入会导致后续节点全部重建。 -
正确示例:
// 正确:使用数据唯一标识(如 id) {items.map(item => <Item key={item.id} />)}
2. 双指针遍历策略
-
头尾指针比对:
- 比对新旧列表头部(Old Start ↔ New Start)和尾部(Old End ↔ New End)。
- 若头尾均不匹配,尝试新头 vs 旧尾、新尾 vs 旧头。
-
Key 映射查找:
若未匹配,通过key
建立哈希表快速定位可复用节点。 -
操作类型:
- 匹配 → 移动节点位置。
- 未匹配 → 新增或删除节点。
示例:
- 旧列表:
[A (key=1), B (key=2), C (key=3)]
- 新列表:
[D (key=4), A (key=1), C (key=3)]
结果:通过key
识别A
和C
可复用,仅插入D
、删除B
。
⚡ 三、性能优化机制
-
Fiber 架构的影响
- 可中断渲染:将 Diff 拆分为多个小任务,允许高优先级更新(如动画)中断当前任务。
- 增量更新:通过链表结构跟踪变更,支持渐进式渲染。
-
开发优化建议
-
避免不必要的渲染:
- 类组件:使用
PureComponent
(浅比较props/state
)。 - 函数组件:用
React.memo
包裹。
- 类组件:使用
-
减少虚拟 DOM 深度:扁平化组件结构,避免嵌套过深。
-
批量更新:合并多次
setState
,减少 DOM 操作次数。
-