Vue2 Diff和Vue 3 Diff实现及底层原理

一、一句话总览(先建立全局认知)

维度 Vue 2 Diff Vue 3 Diff
核心算法 双端比较(双指针) 最长递增子序列(LIS)
比较单位 VNode Block + 动态节点
更新策略 全量递归对比 编译期标记 + 精准更新
时间复杂度 O(n)(最坏仍有重复操作) O(n log n)
性能瓶颈 无法判断"哪些节点一定不变" 只 diff 动态节点
核心思想 运行时尽力猜 编译期提前告诉运行时

二、Vue 2 Diff:双端比较算法(运行时驱动)

1️⃣ Vue 2 的 diff 入口

复制代码
patch(oldVnode, vnode)

如果是同一节点:

复制代码
sameVnode(oldVnode, vnode)

判断条件包括:

  • key

  • tag

  • isComment

  • data 是否存在


2️⃣ 核心算法:双端指针(4 种命中)

复制代码
oldStart →        ← oldEnd
newStart →        ← newEnd

Vue 2 会依次尝试这 4 种匹配:

1️⃣ oldStart vs newStart

2️⃣ oldEnd vs newEnd

3️⃣ oldStart vs newEnd

4️⃣ oldEnd vs newStart

命中就:

  • patch

  • 移动指针

  • 移动 DOM


3️⃣ Vue 2 Diff 示例(key 很重要)

复制代码
<ul>
  <li key="a">A</li>
  <li key="b">B</li>
  <li key="c">C</li>
</ul>

更新为:

复制代码
<ul>
  <li key="b">B</li>
  <li key="a">A</li>
  <li key="c">C</li>
</ul>

Vue 2 做了什么?

  • 通过 key map 找到旧节点

  • 移动 DOM

  • patch 内容

👉 能用,但全靠运行时判断


4️⃣ Vue 2 的问题在哪里?

无法提前知道哪些节点不会变

❌ 每一层都要递归 diff

❌ 静态节点也要参与比较

❌ 复杂列表性能不稳定

Vue 2 的哲学:
"我运行时尽量少改 DOM,但我不知道哪些一定不会变"


三、Vue 3 Diff:Block Tree + LIS(编译期 + 运行时)

这是 Vue 3 真正"质变"的地方。


1️⃣ 核心思想变化(非常重要)

Vue 3 把 diff 的一半工作,提前到"编译期"做完了


2️⃣ 编译期:PatchFlags(关键)

Vue 3 编译模板时:

复制代码
<div>
  <p>{{ msg }}</p>
  <span>static</span>
</div>

会生成类似:

复制代码
createVNode("p", null, msg, PatchFlags.TEXT)

PatchFlags 告诉运行时:

Flag 含义
TEXT 只有文本可能变
CLASS class 可能变
STYLE style 可能变
PROPS props 可能变

👉 没 flag 的节点,直接跳过 diff


3️⃣ Block Tree(块级优化)

Vue 3 会把模板拆成 Block

复制代码
Block
 ├── 动态节点 1
 ├── 动态节点 2

⚠️ Block 里只存动态节点


4️⃣ 运行时 Diff:最长递增子序列(LIS)

当列表乱序时:

复制代码
[a, b, c, d] → [b, a, d, c]

Vue 3 会:

1️⃣ 通过 key 建立映射

2️⃣ 得到旧节点索引数组

复制代码
[1, 0, 3, 2]

3️⃣ 对这个数组求 最长递增子序列

复制代码
LIS = [1, 3]

👉 LIS 里的节点不用动

👉 其他节点才移动 DOM

这一步是 Vue 3 真正快的原因


5️⃣ Vue 3 Diff 过程总结

复制代码
编译期:
  模板 → PatchFlags → Block Tree

运行时:
  只 diff 动态节点
  列表用 LIS 算最少 DOM 移动

四、Vue 2 vs Vue 3 Diff 对比(面试必背)

Vue 2 Vue 3
diff 发生时机 运行时 编译期 + 运行时
静态节点 每次都比 直接跳过
列表优化 双端指针 LIS
移动次数 不确定 理论最少
key 重要性 很重要 更重要

五、底层原理一句话总结(高质量面试回答)

Vue 2 是"运行时驱动的启发式 diff"
Vue 3 是"编译期标记 + 最小化 DOM 操作的确定性 diff"


六、你这个问题在面试怎么说(示例)

Vue 2 主要使用双端指针 diff,通过 key 映射减少 DOM 操作,但所有节点都需要参与比较。

Vue 3 在编译期引入 PatchFlags 和 Block Tree,只对动态节点做 diff,列表更新时结合最长递增子序列,保证 DOM 移动次数最少,整体性能和稳定性都更好。

相关推荐
2501_946224312 小时前
旅行记录应用统计分析 - Cordova & OpenHarmony 混合开发实战
javascript·harmonyos·harvester
傻啦嘿哟2 小时前
隧道代理“请求监控”实战:动态调整采集策略的完整指南
前端·javascript·vue.js
JIngJaneIL2 小时前
基于java + vue个人博客系统(源码+数据库+文档)
java·开发语言·前端·数据库·vue.js·spring boot
C_心欲无痕2 小时前
vue3 - readonly创建只读的响应式对象
前端·javascript·vue.js
Rabi'2 小时前
编译ATK源码
前端·webpack·node.js
SoaringHeart2 小时前
Flutter组件封装:视频播放组件全局封装
前端·flutter
TAEHENGV2 小时前
进度跟踪模块 Cordova 与 OpenHarmony 混合开发实战
android·javascript·数据库
2501_946224313 小时前
旅行记录应用外观设置 - Cordova & OpenHarmony 混合开发实战
javascript·harmonyos·harvester