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 移动次数最少,整体性能和稳定性都更好。

相关推荐
子兮曰4 小时前
OpenClaw入门:从零开始搭建你的私有化AI助手
前端·架构·github
吴仰晖4 小时前
使用github copliot chat的源码学习之Chromium Compositor
前端
1024小神4 小时前
github发布pages的几种状态记录
前端
不像程序员的程序媛6 小时前
Nginx日志切分
服务器·前端·nginx
Daniel李华6 小时前
echarts使用案例
android·javascript·echarts
北原_春希6 小时前
如何在Vue3项目中引入并使用Echarts图表
前端·javascript·echarts
JY-HPS6 小时前
echarts天气折线图
javascript·vue.js·echarts
尽意啊6 小时前
echarts树图动态添加子节点
前端·javascript·echarts
吃面必吃蒜6 小时前
echarts 极坐标柱状图 如何定义柱子颜色
前端·javascript·echarts
O_oStayPositive6 小时前
Vue3使用ECharts
前端·javascript·echarts