Vue数据依赖实现
Vue数据依赖实现原理
Vue的数据依赖实现基于响应式系统,核心是通过Object.defineProperty或Proxy拦截数据读写操作,结合发布-订阅模式自动追踪依赖和触发更新。
核心机制
依赖收集(Dependency Tracking)
在组件渲染过程中,当访问数据属性时,会触发getter拦截器。此时Vue会将当前正在执行的渲染Watcher(或其他计算属性Watcher)记录为该属性的订阅者(依赖)。
// 简化版getter拦截逻辑
function defineReactive(obj, key) {
const dep = new Dep() // 每个属性对应一个Dep(依赖管理器)
let val = obj[key]
Object.defineProperty(obj, key, {
get() {
if (Dep.target) { // 当前活跃的Watcher
dep.depend() // 将Watcher添加到依赖列表
}
return val
},
set(newVal) {
val = newVal
dep.notify() // 通知所有订阅者更新
}
})
}
派发更新(Dependency Notification)
当数据被修改时,setter拦截器会触发依赖管理器(Dep)通知所有关联的Watcher,Watcher会执行重新渲染或计算逻辑。
实现层次
Observer
递归遍历数据对象,为每个属性添加getter/setter拦截。Vue 3改用Proxy实现,解决嵌套对象监听和数组方法拦截问题。
Dep(Dependency)
作为依赖管理器,维护一个订阅者列表(Watcher集合),提供depend()收集依赖和notify()触发更新。
Watcher
作为订阅者,在初始化时通过get()方法主动触发数据getter以收集依赖。当收到更新通知时,执行回调(如组件重新渲染)。
数组的特殊处理
由于Object.defineProperty无法拦截数组方法调用,Vue 2通过重写数组的7个变异方法(如push/pop)实现响应式:
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)
this.__ob__.dep.notify() // 手动触发更新
return result
}
})
Vue 3的优化
使用Proxy替代Object.defineProperty,优势包括:
- 直接监听整个对象而非逐个属性
- 自动处理新增/删除属性
- 完美支持数组索引修改和
length变化 - 减少递归遍历的性能消耗
function reactive(obj) {
return new Proxy(obj, {
get(target, key) {
track(target, key) // 依赖收集
return Reflect.get(target, key)
},
set(target, key, value) {
Reflect.set(target, key, value)
trigger(target, key) // 触发更新
}
})
}






