Vue3 响应式核心源码全解析:Dep、Link 与 track/trigger 完整执行机制详解

逐行解读

Vue3 的响应式系统是整个框架的灵魂,它让开发者能够在不显式调用更新的情况下自动响应数据变化。本文将带你深入阅读 Vue3 的核心响应式模块源码,重点讲解 DepLinktracktrigger 等关键机制,并用通俗的语言串联其工作流程,让你真正理解 Vue3 响应式系统的运行原理。


一、响应式系统的设计思路

Vue3 的响应式系统基于 依赖收集(track)派发更新(trigger) 两大过程:

  • track:在读取响应式数据时记录依赖,建立「谁依赖了谁」的关系;
  • trigger:当依赖的数据发生变化时,通知对应的副作用函数(effect)重新执行。

核心问题是:如何高效、准确地追踪依赖关系并在更新时精准触发?

Vue3 使用 Dep(依赖容器)和 Link(依赖连接)这两个类,配合 targetMap(全局依赖映射表),构建出一个双向的依赖追踪结构。


二、Dep:依赖容器

Dep 是每个响应式属性的依赖中心,负责维护所有订阅它的副作用函数。

typescript 复制代码
export class Dep {
  version = 0
  activeLink?: Link
  subs?: Link
  subsHead?: Link
  sc: number = 0

  constructor(public computed?: ComputedRefImpl | undefined) {}
}

主要属性:

  • version:当前依赖的版本号,每次触发时递增;
  • subs / subsHead:双向链表,记录所有订阅者(副作用函数);
  • activeLink:当前活跃的依赖连接;
  • sc:订阅者数量计数。

Dep 是响应式系统的"核心神经节点",它知道有哪些副作用依赖自己,也能在变化时精确通知它们。


三、Link:Dep 与 Effect 的连接纽带

DepEffect(副作用函数)是多对多关系。
Link 就是这两者之间的桥梁,用于在它们之间建立和维护依赖。

typescript 复制代码
export class Link {
  version: number
  nextDep?: Link
  prevDep?: Link
  nextSub?: Link
  prevSub?: Link

  constructor(public sub: Subscriber, public dep: Dep) {
    this.version = dep.version
  }
}

可以理解为:

  • 每个 Link 同时存在于两条链表中:

    • 一条属于 Effect,记录它依赖的所有 Dep
    • 一条属于 Dep,记录它的所有订阅者。
  • Effect 执行时,Link 会根据访问的属性动态更新依赖关系。

这种 双向链表结构 能快速定位和清理依赖,避免重复依赖和内存泄漏。


四、track:依赖收集

track() 是读取响应式属性时调用的核心函数。它的任务是:
在读取属性时记录当前活跃的副作用函数(activeSub),让它与属性对应的 Dep 建立联系。

vbnet 复制代码
export function track(target: object, type: TrackOpTypes, key: unknown): void {
  if (shouldTrack && activeSub) {
    let depsMap = targetMap.get(target)
    if (!depsMap) {
      targetMap.set(target, (depsMap = new Map()))
    }
    let dep = depsMap.get(key)
    if (!dep) {
      depsMap.set(key, (dep = new Dep()))
      dep.map = depsMap
      dep.key = key
    }
    dep.track({ target, type, key })
  }
}

执行流程如下:

  1. 查找当前对象在 targetMap 中的依赖映射;
  2. 如果没有就创建一个新的;
  3. 获取对应 key 的 Dep
  4. 调用 dep.track() 将当前的 activeSub(副作用函数)与此属性关联。

简单来说,track() 就是在"我读取了这个属性"时,登记"我依赖它"。


五、trigger:派发更新

trigger() 是响应式系统的另一端,它在属性修改时触发依赖更新。

scss 复制代码
export function trigger(target, type, key?, newValue?, oldValue?) {
  const depsMap = targetMap.get(target)
  if (!depsMap) {
    globalVersion++
    return
  }

  const run = (dep: Dep | undefined) => dep && dep.trigger()

  if (type === TriggerOpTypes.CLEAR) {
    depsMap.forEach(run)
  } else {
    const targetIsArray = isArray(target)
    const isArrayIndex = targetIsArray && isIntegerKey(key)

    if (targetIsArray && key === 'length') {
      // 数组长度变化时触发相关依赖
      const newLength = Number(newValue)
      depsMap.forEach((dep, key) => {
        if (key === 'length' || key >= newLength) run(dep)
      })
    } else {
      run(depsMap.get(key))
      if (isArrayIndex) run(depsMap.get(ARRAY_ITERATE_KEY))
      switch (type) {
        case TriggerOpTypes.ADD:
          run(depsMap.get(ITERATE_KEY))
          break
        case TriggerOpTypes.DELETE:
          run(depsMap.get(ITERATE_KEY))
          break
      }
    }
  }
}

核心思路:

  • 修改属性 → 找到对应的 Dep
  • 调用 dep.trigger() 通知所有订阅者;
  • 依赖对应的 Effect(或 Computed)重新执行。

Vue3 针对不同类型(对象、数组、Map、Set)做了细致优化,比如:

  • 数组修改 length 会触发超出新长度的索引依赖;
  • Map 的 ADDDELETE 会额外触发迭代器依赖。

六、globalVersion 与批处理机制

每次响应式变更都会触发:

复制代码
globalVersion++

它用于快速判断计算属性(computed)是否需要重新计算。

此外,Vue3 在触发更新时通过 startBatch()endBatch() 包裹通知过程,实现了批量触发、延迟执行,从而显著减少重复计算和性能浪费。


七、依赖结构图示

为了更直观地理解整个机制,可以把依赖关系抽象为如下结构:

rust 复制代码
Reactive Target
   └── key -> Dep
            ├── Link -> Effect A
            ├── Link -> Computed B
            └── Link -> Effect C
  • track():在访问属性时建立这些箭头;
  • trigger():当属性变化时,沿箭头依次通知所有订阅者。

八、总结

Vue3 的响应式系统在性能和结构上都做了精细设计:

  • 使用 Dep 管理依赖;
  • Link 构建高效的双向链表;
  • 借助 targetMap 构建全局依赖关系;
  • 通过 tracktrigger 完成依赖收集与派发更新;
  • 引入 globalVersion 与批处理优化机制,避免多余计算。

当你理解了这些底层逻辑,再回头看 Vue 的 reactive()computed()watch() 等 API,就会发现------它们都是基于这套机制自然生长出来的。


本文内容由人工智能生成,仅供学习与参考使用,请在实际应用中结合自身情况进行判断。

相关推荐
遗憾随她而去.19 分钟前
前端浏览器缓存深度解析:从原理到实战
前端
万行42 分钟前
企业级前后端认证方式
前端·windows
2501_948120151 小时前
基于Vue 3的可视化大屏系统设计
前端·javascript·vue.js
Jinuss1 小时前
源码分析之React中createFiberRoot方法创建Fiber根节点
前端·javascript·react.js
Jinuss2 小时前
源码分析之React中ReactDOMRoot实现
前端·javascript·react.js
web守墓人2 小时前
【前端】vue3的指令
前端
想起你的日子3 小时前
EFCore之Code First
前端·.netcore
框架图3 小时前
Html语法
前端·html
深耕AI3 小时前
【wordpress系列教程】07 网站迁移与备份
运维·服务器·前端·数据库