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