Vue3 的响应式系统源码级依赖收集与触发更新流程解析
字数 930 2025-11-30 06:12:29

Vue3 的响应式系统源码级依赖收集与触发更新流程解析

我将详细解析 Vue3 响应式系统中依赖收集与触发更新的完整流程。这个过程是 Vue3 响应式系统的核心,理解它有助于掌握 Vue 的响应式原理。

一、响应式基础:Proxy 与 Reflect
Vue3 使用 Proxy 代理目标对象,通过 Reflect 操作对象属性。当访问属性时触发 get 拦截器进行依赖收集,修改属性时触发 set 拦截器进行更新触发。

二、依赖收集(Track)详细流程

  1. 创建响应式对象

    const obj = reactive({ count: 0 })
    

    reactive() 函数内部会创建 Proxy 实例,设置 get/set 拦截器。

  2. effect 函数执行

    effect(() => {
      console.log(obj.count) // 触发 get 拦截器
    })
    

    effect 会立即执行传入的函数,此时活跃的 effect 被设置为当前副作用函数。

  3. get 拦截器触发

    • 判断当前是否有活跃的 effect(activeEffect)
    • 通过 target(目标对象)、key(属性名)建立依赖关系
    • 数据结构:WeakMap(target → Map(key → Set(effect)))
  4. 依赖关系建立过程

    WeakMap
    │
    └── target Object
         │
         └── Map
              │
              └── key: "count"
                   │
                   └── Set
                        │
                        └── [activeEffect]
    

    每个属性都对应一个 effect 集合,确保同一属性的多个 effect 都能被追踪。

三、触发更新(Trigger)详细流程

  1. 属性修改操作

    obj.count = 1 // 触发 set 拦截器
    
  2. set 拦截器触发

    • 通过 Reflect.set 设置新值
    • 根据 target 和 key 从 WeakMap 中查找对应的 effect 集合
    • 避免重复触发:比较新旧值,只有值真正改变时才触发更新
  3. effect 调度执行

    • 将收集到的 effects 加入待执行队列
    • 使用 Set 去重,避免同一 effect 多次执行
    • 根据调度器配置决定同步/异步执行

四、源码级关键实现细节

  1. targetMap 结构

    type Dep = Set<ReactiveEffect>
    type KeyToDepMap = Map<string | symbol, Dep>
    const targetMap = new WeakMap<any, KeyToDepMap>()
    
  2. track 函数核心逻辑

    function track(target: object, key: string | symbol) {
      if (!activeEffect) return
    
      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 Set()))
      }
    
      dep.add(activeEffect)
      activeEffect.deps.push(dep) // 用于清理
    }
    
  3. trigger 函数核心逻辑

    function trigger(target: object, key: string | symbol) {
      const depsMap = targetMap.get(target)
      if (!depsMap) return
    
      const effects = new Set<ReactiveEffect>()
      const addEffects = (dep: Dep | undefined) => {
        if (dep) {
          dep.forEach(effect => {
            effects.add(effect)
          })
        }
      }
    
      addEffects(depsMap.get(key))
    
      // 触发执行
      effects.forEach(effect => {
        if (effect.options.scheduler) {
          effect.options.scheduler(effect)
        } else {
          effect()
        }
      })
    }
    

五、性能优化机制

  1. 分支切换清理:effect 重新执行前清理旧依赖,避免无效依赖残留
  2. 嵌套 effect 支持:通过 effectStack 栈管理嵌套场景
  3. 调度控制:支持同步、异步、批量更新等不同调度策略

六、完整流程示例

const obj = reactive({ count: 0, name: 'vue' })

// 1. 依赖收集
effect(() => {
  console.log(`Count: ${obj.count}`) // 触发 track,建立依赖
})

// 2. 触发更新
obj.count++ // 触发 trigger,执行 effect

这个流程确保了数据变化时,所有依赖该数据的副作用函数都能准确执行,实现了精确的响应式更新。

Vue3 的响应式系统源码级依赖收集与触发更新流程解析 我将详细解析 Vue3 响应式系统中依赖收集与触发更新的完整流程。这个过程是 Vue3 响应式系统的核心,理解它有助于掌握 Vue 的响应式原理。 一、响应式基础:Proxy 与 Reflect Vue3 使用 Proxy 代理目标对象,通过 Reflect 操作对象属性。当访问属性时触发 get 拦截器进行依赖收集,修改属性时触发 set 拦截器进行更新触发。 二、依赖收集(Track)详细流程 创建响应式对象 reactive() 函数内部会创建 Proxy 实例,设置 get/set 拦截器。 effect 函数执行 effect 会立即执行传入的函数,此时活跃的 effect 被设置为当前副作用函数。 get 拦截器触发 判断当前是否有活跃的 effect(activeEffect) 通过 target(目标对象)、key(属性名)建立依赖关系 数据结构:WeakMap(target → Map(key → Set(effect))) 依赖关系建立过程 每个属性都对应一个 effect 集合,确保同一属性的多个 effect 都能被追踪。 三、触发更新(Trigger)详细流程 属性修改操作 set 拦截器触发 通过 Reflect.set 设置新值 根据 target 和 key 从 WeakMap 中查找对应的 effect 集合 避免重复触发:比较新旧值,只有值真正改变时才触发更新 effect 调度执行 将收集到的 effects 加入待执行队列 使用 Set 去重,避免同一 effect 多次执行 根据调度器配置决定同步/异步执行 四、源码级关键实现细节 targetMap 结构 track 函数核心逻辑 trigger 函数核心逻辑 五、性能优化机制 分支切换清理 :effect 重新执行前清理旧依赖,避免无效依赖残留 嵌套 effect 支持 :通过 effectStack 栈管理嵌套场景 调度控制 :支持同步、异步、批量更新等不同调度策略 六、完整流程示例 这个流程确保了数据变化时,所有依赖该数据的副作用函数都能准确执行,实现了精确的响应式更新。