vue依赖收集实现
Vue 依赖收集实现原理
Vue 的依赖收集机制是其响应式系统的核心,主要通过 Observer、Dep 和 Watcher 三个类协作完成。
核心流程
Observer 数据劫持
通过 Object.defineProperty 或 Proxy 拦截对象属性的读写操作。在 getter 中收集依赖,在 setter 中触发更新。
class Observer {
constructor(value) {
this.walk(value)
}
walk(obj) {
Object.keys(obj).forEach(key => {
defineReactive(obj, key, obj[key])
})
}
}
Dep 依赖管理
每个被观察的属性会创建一个 Dep 实例,用于存储所有依赖该属性的 Watcher。
class Dep {
constructor() {
this.subs = new Set()
}
addSub(watcher) {
this.subs.add(watcher)
}
notify() {
this.subs.forEach(watcher => watcher.update())
}
}
Watcher 观察者
在计算属性、模板编译等场景创建,保存更新逻辑。触发 getter 时会被添加到对应 Dep 中。
class Watcher {
constructor(vm, expOrFn, cb) {
this.vm = vm
this.getter = parsePath(expOrFn)
this.cb = cb
this.value = this.get()
}
get() {
Dep.target = this // 标记当前watcher
const value = this.getter.call(this.vm, this.vm)
Dep.target = null
return value
}
update() {
this.run()
}
run() {
const value = this.get()
if (value !== this.value) {
const oldValue = this.value
this.value = value
this.cb.call(this.vm, value, oldValue)
}
}
}
依赖收集过程
- 初始化阶段通过
Observer递归地将数据对象转为响应式 - 模板编译时遇到插值表达式或指令会创建
Watcher Watcher在求值过程中触发属性的 getter- getter 中通过
Dep.target判断当前是否有活跃的Watcher - 如果存在则将
Watcher添加到属性的Dep中 - 数据变更时触发 setter,通过
Dep.notify()通知所有关联Watcher更新
数组处理特殊逻辑
对于数组类型,Vue 通过拦截以下方法实现响应式:
- push/pop/shift/unshift/splice/sort/reverse 通过重写这些方法,在变更数组时手动触发通知。
const arrayProto = Array.prototype
const arrayMethods = Object.create(arrayProto)
;['push','pop','shift','unshift','splice','sort','reverse'].forEach(method => {
const original = arrayProto[method]
arrayMethods[method] = function(...args) {
const result = original.apply(this, args)
const ob = this.__ob__
ob.dep.notify()
return result
}
})
现代实现改进
Vue 3 改用 Proxy 实现依赖收集,优势包括:
- 可检测新增/删除属性
- 更好的数组变化检测
- 性能优化避免递归遍历
Proxy 版本的依赖收集通过 track 和 trigger 函数实现,原理类似但实现更简洁。







