第 27 题:Vue3 的 Diff 算法核心原理(双端 Diff、PatchFlags、Block Tree、静态提升)
我会按照结构:
核心回答 → 深度讲解 → 动画级别示意 → PatchFlags & 静态提升 → 面试官追问 → 高分总结
一、核心回答(面试官最想听)
Vue3 的 Diff 算法是基于 双端对比 + 最长递增子序列(LIS)优化 + 静态提升 + block tree + patchFlags 的。
比 Vue2 快的原因:
- 静态节点跳过 diff
- PatchFlags 减少不必要对比
- block tree 限制 diff 范围
- 优化后的双端 Diff + LIS
- 模板编译阶段极大地减少了运行时工作量
二、深度原理(深入讲,高分)
1️⃣ Vue3 Diff = "双端 Diff + LIS"
Vue3 的核心 Diff 逻辑如下:
第一阶段:从头开始比对(前端指针)
如果新旧头节点是相同 key,就 patch 并移动:
css
旧:[A B C D]
新:[A B E C D]
从头开始:
A → match
B → match
遇到 C / E 不同,停止
第二阶段:从尾开始比对(后端指针)
css
旧:[A B C D]
新:[A B E C D]
从尾开始:
D → match
第三阶段:处理中间乱序部分(核心:LIS)
只对中间这一段做 Diff:
css
旧:[C]
新:[E C]
Vue3 会:
- 建立 key → newIndex 的映射
- 找出新列表中的最长递增子序列(LIS)
- 其他节点移动或新建
为什么用 LIS?
保持最长不动序列,减少 DOM 移动次数,这是最优移动策略。
2️⃣ PatchFlags(编译器给每个节点打标签)
PatchFlags 是 Vue3 性能飞越的核心。
例如:
css
<div :class="theme">{{ msg }}</div>
编译器会这样标注:
:class是动态绑定 → PatchFlag: CLASSmsg是动态文本 → PatchFlag: TEXT
运行时只 patch 有变化的部分。
⚡ 性能极强,因为跳过所有静态节点。
3️⃣ 静态提升(static hoisting)
Vue3 会把模板中永远不会变的 DOM 结构提前提升到外部:
css
<div><span>Hello</span></div>
静态提升后:
ini
const _hoisted_1 = h("span", "Hello")
每次渲染:
- 静态 vnode 复用
- 不重新创建不 diff vnode
Vue2 每次 render 都重新创建所有 vnode → 低效
4️⃣ Block Tree(Vue3 的革命性设计)
Vue3 把组件拆成一个个 block,每个 block 只 diff 自己内部的动态节点列表。
示例:
css
<div>
<p>静态</p>
<p>{{msg}}</p>
</div>
Vue 会生成类似结构:
scss
Block root: only tracks [p(msg)]
静态部分完全跳过。
Diff 范围更小,性能更高。
5️⃣ 为什么 Vue3 比 React18 更快?
因为 Vue3 使用 模板编译提前做大量优化:
- 静态提升
- PatchFlags
- Block Tree
- 编译期确定最小更新单元
React 是纯运行时,不知道谁是动态的,每次 render 都 diff 全量子树。
三、代码示例:Diff 执行过程可视化动画(面试官会点头)
旧节点:
css
A B C D E F
新节点:
css
A C E B D G F
1. 双端比对阶段:
ini
A = A → ok
F = F → ok
中间区域:
旧:B C D E
新:C E B D G
2. 建立映射:
css
C → 0
E → 1
B → 2
D → 3
G → 4
3. 算 LIS → [0,1,3] = C, E, D
保持不动的节点:C、E、D
要移动的:B
要新增的:G
四、面试官追问 & 高分回答
❓1. Vue3 Diff 与 Vue2 Diff 的区别?
你可以直接这样答(高分):
Vue2
- 全量双端 diff
- 无 PatchFlags
- 无静态提升
- 无 block tree
Vue3
- 更智能的 diff:双端 + LIS
- 编译器优化:PatchFlags + static hoisting
- block tree:减少 diff 范围
总结:Vue2 是"纯运行时 diff",Vue3 是"编译 + 运行时混合优化 diff"。
❓2. 为什么需要最长递增子序列?
为了减少 DOM 移动次数。
没有 LIS:
- 每个节点都要移动 → 最坏 O(n²)
有 LIS:
- 移动最少,趋近优化到 O(n log n)
❓3. 什么情况下 Vue 不用 Diff?
以下情况直接复用:
- 静态节点
- patchFlags 标识完全静态的部分
- v-once(只渲染一次)
- keyed children 未发生任何动态变化
❓4. key 的作用是什么?
key 是 diff 的灵魂,让节点可复用、有身份。
key 不唯一会导致:
- patch 错乱
- 意料之外的 DOM 复用
- input 数据错位
五、高分总结(背下来能打工地级别)
Vue3 的 diff 是优化后的双端 diff + LIS 最长递增子序列算法,并结合编译器的 PatchFlags、静态提升、Block Tree,大幅减少 vnode 创建数量和 diff 范围,是目前前端框架中最激进的运行 + 编译混合优化方式,比 Vue2 和 React 都更快。