Vue 的虚拟 DOM Diff 算法是怎样的?
Vue Diff 策略
Vue 的 Diff 算法基于同层比较,使用双端比较(Vue2)和最长递增子序列(Vue3)优化。
Vue2 双端比较
使用 4 个指针:旧头、旧尾、新头、新尾
旧:[A, B, C, D]
↑head ↑tail
新:[D, A, B, C]
↑head ↑tail
比较过程:
1. 旧头 vs 新头 → A vs D ✗
2. 旧尾 vs 新尾 → D vs C ✗
3. 旧头 vs 新尾 → A vs C ✗
4. 旧尾 vs 新头 → D vs D ✓ → 移动 D 到前面
重复直到完成Vue3 快速 Diff + 最长递增子序列
Vue3 改进了 Diff 算法:
- 从头同步:从头部开始,遇到不同的就停止
- 从尾同步:从尾部开始,遇到不同的就停止
- 处理中间部分:
- 如果新节点多 → 挂载新节点
- 如果旧节点多 → 卸载旧节点
- 如果都有 → 使用最长递增子序列最小化 DOM 移动
旧:[A, B, C, D, E]
新:[A, D, C, B, E]
头部同步:A ✓
尾部同步:E ✓
中间:旧 [B, C, D] → 新 [D, C, B]
新中间在旧中间的位置:[3, 2, 1](D=3, C=2, B=1)
最长递增子序列:[2, 1](C, B 不需要移动)
只需移动 DKey 的作用
// 有 key:精确匹配,最小化 DOM 操作
// 旧:[{key:1, v:A}, {key:2, v:B}]
// 新:[{key:2, v:B}, {key:1, v:A}]
// 结果:移动节点,不销毁重建
// 无 key:按位置比较,内容不同就更新
// 旧:[A, B]
// 新:[B, A]
// 结果:A→B(更新DOM),B→A(更新DOM),2次DOM操作Vue vs React Diff
| 特性 | Vue | React |
|---|---|---|
| 同层比较 | ✅ | ✅ |
| Key | ✅ 必须 | ✅ 必须 |
| 算法 | 双端/最长递增子序列 | 右侧遍历 |
| 移动优化 | 最小移动 | 按需移动 |
| 复杂度 | O(n) | O(n) |
Vue3 的优化
- 静态提升:静态节点只创建一次
- PatchFlag:编译时标记动态节点,Diff 时只比较动态部分
- Block Tree:将动态节点拍平为数组,跳过静态节点
- 缓存事件处理器:内联函数只创建一次
// 编译后的渲染函数带有 PatchFlag
createVNode('div', { class: 'static' }, 'hello', PatchFlags.TEXT)
// Diff 时只比较 text,跳过 class