《Vue.js设计与实现》笔记 第11章:快速 Diff 算法
Vue.js设计与实现 第11章:快速 Diff 算法
本章导读
Vue3 最终 Diff 方案:
Fast Diff
+
Longest Increasing Subsequence
(LIS)
目标:
减少DOM移动
提高更新性能
整体流程:
预处理
↓
同步头部
↓
同步尾部
↓
处理中间乱序部分
↓
LIS
↓
移动DOM
一、为什么需要快速Diff
双端Diff的问题
例如:
A B C D E
↓
D C B A E
虽然:
节点都存在
但:
大量查找
大量移动
性能仍不理想。
Vue3优化目标:
减少查找
减少移动
二、快速Diff整体结构
分为五步:
1 同步前置节点
2 同步后置节点
3 处理新增
4 处理删除
5 处理中间乱序
三、同步前置节点
示例
旧:
A B C D
新:
A B E F
从头开始:
A == A
B == B
直接:
patch()
继续:
C != E
停止。
结果:
前面公共部分已处理
四、同步后置节点
示例
旧:
A B C D
新:
E F C D
从尾开始:
D == D
C == C
直接:
patch()
继续:
B != F
停止。
结果:
后面公共部分已处理
五、为什么先处理头尾
因为真实项目中:
列表变化
通常发生在中间
例如:
头部稳定
尾部稳定
中间变化
这样:
大量节点无需参与Diff
六、处理新增节点
示例
旧:
A B
新:
A B C D
头尾同步后:
旧节点已处理完
剩余:
C D
直接:
patch(
null,
vnode
)
挂载。
七、处理删除节点
示例
旧:
A B C D
新:
A B
同步后:
新节点已结束
剩余:
C D
执行:
unmount()
删除即可。
八、中间乱序节点
最复杂情况:
旧:
A B C D E
新:
A D C B E
头尾同步后:
处理中间:
B C D
↓
D C B
这部分才是真正Diff重点。
九、建立Key索引表
为什么建立Map
旧方案:
查找节点
O(n)
Vue3:
Map<
key,
index
>
例如:
{
D:2,
C:1,
B:0
}
查找:
map.get(key)
复杂度:
O(1)
十、source数组
作用
记录:
新节点
对应旧节点位置
例如:
旧:
B C D
新:
D C B
结果:
source = [
2,
1,
0
]
表示:
D来自旧索引2
C来自旧索引1
B来自旧索引0
十一、判断是否需要移动
示例
source = [
0,
1,
2
]
说明:
完全递增
不需要移动。
示例
source = [
2,
1,
0
]
说明:
顺序已改变
需要移动。
十二、最长递增子序列(LIS)
核心优化。
什么是LIS
Longest Increasing Subsequence
最长递增子序列
例如:
[
2,
3,
1,
5,
4
]
LIS:
[
2,
3,
5
]
因为:
保持递增
且:
长度最长
十三、为什么需要LIS
示例
source = [
2,
3,
1,
4
]
LIS:
[
2,
3,
4
]
表示:
这些节点位置正确
无需移动。
只移动:
1
对应节点。
十四、LIS核心思想
保留不动节点
例如:
A B C D E
变化后:
A D B C E
LIS:
B C
说明:
B
C
已经处于正确顺序
无需移动。
只移动:
D
十五、倒序遍历
Vue3最终:
for(
i = count-1
i >=0
i--
)
原因:
插入节点时
锚点稳定
方便移动。
十六、移动节点
找到锚点
例如:
anchor =
nextVNode.el
执行:
insert(
vnode.el,
container,
anchor
)
完成:
DOM移动
十七、完整流程
同步头部
↓
同步尾部
↓
新增
↓
删除
↓
建立索引表
↓
生成source
↓
计算LIS
↓
移动节点
十八、时间复杂度
查找
Map:
O(1)
遍历
O(n)
LIS
O(n log n)
最终:
O(n)
级别。
远优于:
简单Diff
O(n²)
十九、Vue3为什么快
核心原因:
1 Map索引
2 头尾预处理
3 LIS优化
4 最少DOM移动
真正节省的不是:
JS计算
而是:
DOM操作
第二十章核心执行流程
旧VNode
↓
同步头
↓
同步尾
↓
处理中间
↓
Map
↓
source
↓
LIS
↓
移动DOM
↓
完成更新
第11章核心知识图谱
Fast Diff
│
├── 同步头部
├── 同步尾部
├── 新增
├── 删除
│
└── 中间乱序
│
▼
Map
│
▼
source
│
▼
LIS
│
▼
DOM Move
高频面试题
Vue3为什么不用双端Diff?
因为:
Fast Diff
+
LIS
性能更好
Fast Diff优化点有哪些?
同步头
同步尾
Map索引
LIS
source数组作用?
记录:
新节点对应旧节点位置
什么是LIS?
最长递增子序列。
为什么使用LIS?
找到:
不需要移动的节点
减少:
DOM移动次数
LIS优化的本质是什么?
让尽可能多节点保持原位
Vue3 Diff时间复杂度?
O(n)
实际:
O(n log n)
主要来自LIS。
为什么倒序遍历?
为了:
保证锚点稳定
方便插入。
本章总结
Vue3 Fast Diff流程:
同步头
↓
同步尾
↓
新增
↓
删除
↓
Map索引
↓
source数组
↓
LIS
↓
DOM移动
核心优化:
Map
+
LIS
目标:
最少DOM移动
这是 Vue3 Diff 算法性能优于 Vue2 的关键原因。
理解:
source
LIS
为什么要倒序遍历
基本就掌握了 Vue3 Diff 的核心思想。

浙公网安备 33010602011771号