第 28 题:Vue3 的 Diff 算法核心原理(双端 Diff、PatchFlags、Block Tree、静态提升)

第 27 题:Vue3 的 Diff 算法核心原理(双端 Diff、PatchFlags、Block Tree、静态提升)

我会按照结构:
核心回答 → 深度讲解 → 动画级别示意 → PatchFlags & 静态提升 → 面试官追问 → 高分总结


一、核心回答(面试官最想听)

Vue3 的 Diff 算法是基于 双端对比 + 最长递增子序列(LIS)优化 + 静态提升 + block tree + patchFlags 的。

比 Vue2 快的原因:

  1. 静态节点跳过 diff
  2. PatchFlags 减少不必要对比
  3. block tree 限制 diff 范围
  4. 优化后的双端 Diff + LIS
  5. 模板编译阶段极大地减少了运行时工作量

二、深度原理(深入讲,高分)


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: CLASS
  • msg 是动态文本 → 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 都更快。

相关推荐
前端一课1 小时前
【vue高频面试题】第 23 题:Vue3 自定义指令(directive)完整解析
前端·面试
前端一课1 小时前
【vue高频面试题】第 21 题:Vue3 中的 Slot(插槽)— 基础、原理、使用场景、面试必问点
前端·面试
前端一课1 小时前
第 24 题:Vue3 的组件通信方式(props / emit / v-model / provide-inject / expose / eventBus
前端·面试
前端一课1 小时前
第 22 题:Teleport 的作用、原理和使用场景
前端·面试
前端一课1 小时前
第 29 题:Vue3 的 Teleport 原理(跨层级 DOM 挂载技术)
前端·面试
前端一课1 小时前
第 31 题:Vue3 中 watchEffect 的原理(依赖自动追踪 + 清理机制 + ReactiveEffect 全流程)
前端·面试
前端一课1 小时前
第 256 题:Vue3 的异步组件(defineAsyncComponent)+ Suspense 原理与面试高频点
前端·面试
前端一课1 小时前
第 27 题:Vue3 + TS 类型推断(Props 类型推导、Emit 类型推导、Setup 返回值类型)
前端·面试
是罐装可乐1 小时前
前端架构知识体系:通过发布-订阅者模式解耦路由和请求
前端·架构·vue·路由