vue依赖收集实现
Vue 依赖收集实现原理
Vue 的依赖收集是通过响应式系统实现的,核心是 Observer、Dep 和 Watcher 三个类的协作。
核心类与流程
Observer
负责将数据对象转换为响应式对象,通过 Object.defineProperty 或 Proxy 拦截属性的读取和修改操作。在属性被访问时触发依赖收集,修改时触发更新通知。
class Observer {
constructor(value) {
this.value = value
this.walk(value)
}
walk(obj) {
Object.keys(obj).forEach(key => {
defineReactive(obj, key, obj[key])
})
}
}
Dep(依赖管理器)
每个响应式属性都有一个对应的 Dep 实例,用于存储所有依赖该属性的 Watcher。提供 depend 方法收集依赖,notify 方法通知更新。
class Dep {
constructor() {
this.subs = new Set()
}
depend() {
if (Dep.target) {
this.subs.add(Dep.target)
}
}
notify() {
this.subs.forEach(watcher => watcher.update())
}
}
Dep.target = null // 全局唯一的当前 Watcher
Watcher(观察者)
代表一个依赖关系,在计算属性、模板渲染等场景中创建。初始化时会触发属性的 getter,从而将自身添加到 Dep 中。
class Watcher {
constructor(getter) {
this.getter = getter
this.get()
}
get() {
Dep.target = this
this.value = this.getter() // 触发依赖收集
Dep.target = null
return this.value
}
update() {
this.get()
}
}
依赖收集流程
- 数据劫持
defineReactive为每个属性创建闭包的Dep实例,并在getter中调用dep.depend()。
function defineReactive(obj, key, val) {
const dep = new Dep()
Object.defineProperty(obj, key, {
get() {
if (Dep.target) {
dep.depend() // 收集当前 Watcher
}
return val
},
set(newVal) {
if (newVal === val) return
val = newVal
dep.notify() // 通知更新
}
})
}
-
Watcher 初始化
当组件渲染或计算属性被访问时,创建Watcher实例并执行getter函数,触发属性的getter。 -
依赖关联
Dep.target指向当前Watcher,通过dep.depend()将Watcher添加到Dep.subs中。
数组的处理
对于数组,Vue 通过拦截原生方法(如 push、pop)触发更新,并对数组元素递归执行 observe:
const arrayProto = Array.prototype
const arrayMethods = Object.create(arrayProto)
;['push', 'pop', 'shift'].forEach(method => {
const original = arrayProto[method]
arrayMethods[method] = function(...args) {
const result = original.apply(this, args)
this.__ob__.dep.notify() // 通知更新
return result
}
})
Vue 3 的优化
Vue 3 改用 Proxy 实现响应式,依赖收集逻辑类似,但不再需要递归劫持属性,而是按需动态收集:

function reactive(obj) {
return new Proxy(obj, {
get(target, key, receiver) {
track(target, key) // 依赖收集
return Reflect.get(target, key, receiver)
},
set(target, key, value, receiver) {
Reflect.set(target, key, value, receiver)
trigger(target, key) // 触发更新
}
})
}
关键点总结
- 惰性依赖收集:仅在
Watcher访问属性时建立依赖关系。 - 精准更新:每个属性维护独立的
Dep,避免不必要的重新渲染。 - 避免循环依赖:通过
Dep.target栈管理嵌套Watcher。






