Vue 3 响应式依赖收集 (Track/Trigger) 与快速 Diff (最长递增子序列 LIS) 步进推演器

配套《Vue3架构分析(三)(四)》的运行时交互实验室:基于 Rust Wasm 单步推演 patchKeyedChildren 五阶段快速 Diff、最长递增子序列 (getSequence) 锚点求解与 ReactiveEffect 依赖图谱。

⚡ 一、Vue 3 快速 Diff 算法(双端预处理 + 最长递增子序列 LIS)单步推演

基于 Rust Wasm 还原 Vue 3 源码 `patchKeyedChildren` 与 `getSequence`:直观观察双端指针 (i, e1, e2) 与最长递增子序列锚点

DOM 移动: 0新增挂载: 0卸载节点: 0
0. Initi = 0 · e1 = 0 · e2 = 0
💡 正在初始化 Rust Wasm 快速 Diff 引擎...
旧子节点列表 (c1)
#0Aie1
#1B
#2C
#3D
#4E
#5F
#6G
新子节点列表 (c2) — 绿色代表命中 LIS 最长递增子序列免移动锚点
#0Aie2
#1B
#2E
#3C
#4D
#5H
#6F
#7G
🖥️ 当前步骤真实 DOM 树节点顺序快照
DOM[0]<li key="A">
DOM[1]<li key="B">
DOM[2]<li key="C">
DOM[3]<li key="D">
DOM[4]<li key="E">
DOM[5]<li key="F">
DOM[6]<li key="G">

🔗 二、Vue 3 响应式依赖收集 (track) 与精确派发更新 (trigger) 演练台

点击下方按钮修改响应式变量,观察 targetMap -> depsMap -> Dep(ReactiveEffect) 如何精准触发对应副作用函数

msg: "Hello Yunsin"RefImpl<string>
🎯 Component Render Effect (setupRenderEffect)
🎯 ComputedRefImpl (fullGreeting)
🎯 Watcher Pre-Flush Effect (watch(msg))
count: 1RefImpl<number>
🎯 Component Render Effect (setupRenderEffect)
theme: "dark"ReactiveProxy.theme
🎯 Component Render Effect (setupRenderEffect)
1. Component Render Effect (setupRenderEffect)Runs: 1

在首次渲染执行 render() 时通过 track() 收集了 [msg, count, theme] 三个依赖,任一变更均调度 queueJob 更新视图。

2. ComputedRefImpl (`greeting = computed(() => msg.value)`)Evaluations: 1

仅在 Dep(msg) 集合中注册!修改 count 或 theme 时完全不会触发该计算属性重新求值。

3. Watcher Callback (`watch(() => msg.value, cb)`)Triggered: 0

仅订阅 Dep(msg),当 msg 发生变更时通过 scheduler 触发监听回调。

💡 省钱实战方案 · 开发者精选资源官方合作链接 · 由对应平台提供全部售后保障

图解 Vue 3 运行时核心:快速 Diff 五步法与最长递增子序列 (LIS)

本交互式实验室与《Vue3架构分析(三)—— 渲染器与App设计》《Vue3架构分析(四)—— 组件实例的创建和初始化》紧密联动。在浏览器中操作真实 DOM 的移动(insertBefore)远比纯 JS 对象的计算昂贵,因此 Vue 3 在更新带 key 的 v-for 列表时采用了五阶段算法:

  • 阶段一(头部同步): 指针 i 从 0 开始向右扫描,只要新旧节点 key 相同便原地 patch。
  • 阶段二(尾部同步): 指针 e1(旧尾)与 e2(新尾)同时向左扫描,跳过尾部相同节点。
  • 阶段三 & 四(纯新增或纯卸载): 若 i > e1 说明只需挂载新增节点;若 i > e2 说明只需卸载多余旧节点。
  • 阶段五(乱序区间 + 最长递增子序列 LIS): 利用 getSequence(newIndexToOldIndexMap) 找出相对顺序不变的最长子序列作为不动锚点,倒序遍历仅对非 LIS 节点调用 hostInsert,实现数学意义上的最少 DOM 移动。

常见问题与实战解答 (FAQ)

Q1 Vue 3 的快速 Diff 算法相比 Vue 2 的双端 Diff 有什么核心改进?
•
Vue 3 借鉴了 ivi 和 inferno 框架的快速 Diff 思想:首先通过前置同步(Sync from start,指针 i 从左向右)与后置同步(Sync from end,指针 e1/e2 从右向左)快速跳过头尾未发生位移的相同节点。
•
仅当中间存在乱序未知子序列时,才构建 newIndexToOldIndexMap 并使用贪心+二分查找求出【最长递增子序列 (LIS)】,凡是命中 LIS 的节点均保持原地不动,从而保证在 O(n log n) 时间内实现最小次数的真实 DOM 移动操作。
Q2 Vue 3 响应式系统是如何组织 targetMap、depsMap 和 Dep 的?
•
Vue 3 内部使用三层数据结构存储依赖关系:最外层 targetMap 是一个 WeakMap(键为响应式原始对象 target,值为 depsMap,当 target 无引用时可被 GC 自动回收)。
•
第二层 depsMap 是一个 Map(键为对象的属性名 key,值为 Dep 集合)。
•
第三层 Dep 是一个存储所有订阅该属性的 ReactiveEffect 实例的集合。
•
读取属性触发 track() 收集依赖,修改属性触发 trigger() 派发更新。