一句话核心结论
Vue 2 是运行时驱动的双端 diff
Vue 3 是编译期标记 + 运行时最小化更新(LIS)
Vue 2 Diff(双端比较)
核心算法
-
双端指针 diff
-
运行时递归对比整棵 VNode 树
oldStart → ← oldEnd
newStart → ← newEnd
4 种命中策略
-
oldStart vs newStart
-
oldEnd vs newEnd
-
oldStart vs newEnd
-
oldEnd vs newStart
- key → 建立 map 查找
特点
-
所有节点都会参与 diff
-
静态节点也要比较
-
列表乱序时 DOM 移动次数不稳定
优缺点
✅ 实现简单、直观
❌ 大模板 / 大列表性能不稳定
Vue 3 Diff(Block + LIS)
编译期优化(关键差异)
-
PatchFlags:标记哪些会变
-
Block Tree:只收集动态节点
PatchFlags.TEXT
PatchFlags.CLASS
PatchFlags.PROPS
运行时 diff
-
只 diff 动态节点
-
列表更新使用 最长递增子序列(LIS)
-
保证 最少 DOM 移动
特点
-
静态节点完全跳过
-
diff 更"确定性"
-
性能更稳定
Vue 2 vs Vue 3 对比表(速记)
| 维度 | Vue 2 | Vue 3 |
|---|---|---|
| diff 时机 | 运行时 | 编译期 + 运行时 |
| 核心算法 | 双端指针 | LIS |
| 静态节点 | 参与 diff | 直接跳过 |
| 列表性能 | 不稳定 | 最优 |
| key 作用 | 重要 | 非常重要 |
面试标准回答模板(直接背)
Vue 2 的 diff 主要是运行时的双端比较算法,通过 key 减少 DOM 操作,但所有节点都需要参与比较。
Vue 3 在编译期引入 PatchFlags 和 Block Tree,只对动态节点进行 diff,并在列表更新时通过最长递增子序列算法,保证 DOM 移动次数最少,因此性能和稳定性都更好。
高频追问(你一定会被问)
Q1:为什么 Vue 3 一定要用 key?
👉 LIS 必须依赖稳定 key 才能算出"最少移动"
Q2:Vue 3 diff 还需要递归吗?
👉 需要,但只在 Block 内的动态节点
Q3:Vue 3 为什么更快?
👉 不是算法更快,是 diff 的节点更少