依赖收集与触发
响应式系统的核心机制是依赖收集(track)与依赖触发(trigger),实现数据与视图的绑定。
Dep 与 Watcher
JavaScript
let uid = 0
class Dep {
constructor() {
this.id = uid++
this.subs = []
}
addSub(sub) {
this.subs.push(sub)
}
depend() {
if (Dep.target) this.addSub(Dep.target)
}
notify() {
this.subs.slice().forEach(sub => sub.update())
}
}
Dep.target = null
依赖收集过程
数据被读取时触发 getter,收集当前活跃的 Watcher:
JavaScript
function defineReactive(obj, key, val) {
const dep = new Dep()
Object.defineProperty(obj, key, {
get() {
// 组件渲染时,全局 Dep.target 指向当前 Watcher
if (Dep.target) {
dep.depend() // 收集依赖
}
return val
},
set(newVal) {
val = newVal
dep.notify() // 触发更新
}
})
}
Watcher 类
JavaScript
class Watcher {
constructor(vm, expOrFn, cb) {
this.vm = vm
this.cb = cb
this.getter = typeof expOrFn === 'function' ? expOrFn : () => vm[expOrFn]
this.value = this.get()
}
get() {
Dep.target = this
const value = this.getter.call(this.vm)
Dep.target = null
return value
}
update() {
const oldValue = this.value
this.value = this.get()
this.cb.call(this.vm, this.value, oldValue)
}
}
Vue 3 的 track 与 trigger
Vue 3 使用 WeakMap + Map + Set 存储依赖关系:
JavaScript
const targetMap = new WeakMap()
function track(target, key) {
if (!Dep.target) 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(Dep.target)
}
function trigger(target, key) {
const depsMap = targetMap.get(target)
if (!depsMap) return
const dep = depsMap.get(key)
if (dep) dep.forEach(effect => effect())
}
计算属性的依赖缓存
JavaScript
const fullName = computed(() => `${firstName.value} ${lastName.value}`)
// firstName 和 lastName 的 getter 执行时收集了 computed 作为依赖
// 只有两者之一变化时,fullName 才会重新计算
要点总结
- getter 中调用
dep.depend()收集当前 Watcher - setter 中调用
dep.notify()通知所有 Watcher 更新 - Vue 2 使用 Dep + Watcher 类实现
- Vue 3 使用
WeakMap存储依赖关系,更高效的 track/trigger
📝 发现内容有误?点击此处直接编辑