Vue数据依赖实现
Vue数据依赖实现原理
Vue的数据依赖实现基于响应式系统,核心是通过Object.defineProperty或Proxy拦截数据读写操作,结合观察者模式完成依赖收集和触发更新。
核心模块分工
响应式系统
通过Observer类递归地将数据对象转换为可观测对象,为每个属性创建Dep实例(依赖收集器)。当属性被访问时触发getter,当前Watcher会被记录到Dep中;当属性修改时触发setter,通知所有关联的Watcher更新。
依赖收集器(Dep)
每个响应式属性对应一个Dep实例,用于存储所有依赖该属性的Watcher。关键方法包括:
depend():将当前正在计算的Watcher添加到依赖列表notify():通知所有订阅者执行更新
观察者(Watcher)
作为桥梁连接模板与数据,分为渲染Watcher、计算属性Watcher和用户自定义Watcher。在初始化时会触发get()方法,通过pushTarget()设置全局当前Watcher,触发数据属性的getter完成依赖收集。
实现代码示例
// 简化版Observer实现
class Observer {
constructor(value) {
this.value = value
this.walk(value)
}
walk(obj) {
Object.keys(obj).forEach(key => {
defineReactive(obj, key, obj[key])
})
}
}
// 响应式属性定义
function defineReactive(obj, key, val) {
const dep = new Dep()
Object.defineProperty(obj, key, {
get() {
if (Dep.target) {
dep.depend()
}
return val
},
set(newVal) {
if (newVal === val) return
val = newVal
dep.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
// 观察者
class Watcher {
constructor(getter) {
this.getter = getter
this.value = this.get()
}
get() {
Dep.target = this
const value = this.getter()
Dep.target = null
return value
}
update() {
this.value = this.get()
}
}
数组的特殊处理
对于数组类型,Vue通过拦截7个变更方法实现响应式:
const arrayProto = Array.prototype
const arrayMethods = Object.create(arrayProto)
const methodsToPatch = [
'push', 'pop', 'shift', 'unshift',
'splice', 'sort', 'reverse'
]
methodsToPatch.forEach(method => {
const original = arrayProto[method]
def(arrayMethods, method, function mutator(...args) {
const result = original.apply(this, args)
const ob = this.__ob__
ob.dep.notify()
return result
})
})
Vue 3的优化改进
Vue 3使用Proxy替代Object.defineProperty,解决了以下问题:

- 无法检测新增/删除的属性
- 数组索引修改和长度变化需要特殊处理
- 减少了递归遍历初始化的性能消耗
function reactive(target) {
return new Proxy(target, {
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)
return true
}
})
}






