响应式系统与依赖追踪
Vue响应式基于 Object.defineProperty 劫持getter/setter,通过 Dep 类实现依赖收集与派发更新。
defineReactive 核心实现
JavaScript
function defineReactive(obj, key, val, customSetter, shallow) {
const dep = new Dep() // 每个属性独立Dep
const property = Object.getOwnPropertyDescriptor(obj, key)
if (property && property.configurable === false) return
const getter = property && property.get
const setter = property && property.set
// 递归观测子对象
let childOb = !shallow && observe(val)
Object.defineProperty(obj, key, {
enumerable: true,
configurable: true,
get: function reactiveGetter() {
const value = getter ? getter.call(obj) : val
// 依赖收集
if (Dep.target) {
dep.depend()
if (childOb) {
childOb.dep.depend()
if (Array.isArray(value)) {
dependArray(value)
}
}
}
return value
},
set: function reactiveSetter(newVal) {
const value = getter ? getter.call(obj) : val
if (newVal === value || (newVal !== newVal && value !== value)) return
if (setter) {
setter.call(obj, newVal)
} else {
val = newVal
}
// 新值观测
childOb = !shallow && observe(newVal)
// 派发更新
dep.notify()
}
})
}
getter收集依赖,setter派发更新,每个属性有独立 Dep 实例。
Dep 依赖管理类
JavaScript
let uid = 0
class Dep {
constructor() {
this.id = uid++
this.subs = [] // Watcher数组
}
// 添加订阅者
addSub(sub: Watcher) {
this.subs.push(sub)
}
// 移除订阅者
removeSub(sub: Watcher) {
remove(this.subs, sub)
}
// 收集依赖
depend() {
if (Dep.target) {
Dep.target.addDep(this)
}
}
// 派发更新
notify() {
const subs = this.subs.slice()
for (let i = 0, l = subs.length; i < l; i++) {
subs[i].update()
}
}
}
Dep.target = null // 全局唯一活跃Watcher
Dep 管理属性的所有订阅Watcher,使用副本防止遍历时修改数组。
Watcher 观察者
JavaScript
class Watcher {
constructor(vm, expOrFn, cb, options) {
this.vm = vm
this.cb = cb
this.deps = [] // 依赖的Dep列表
this.depIds = new Set()
this.getter = parsePath(expOrFn) // 解析表达式
this.value = this.get()
}
get() {
pushTarget(this) // 设置Dep.target
const vm = this.vm
value = this.getter.call(vm, vm) // 执行函数触发getter
return value
}
addDep(dep: Dep) {
const id = dep.id
if (!this.depIds.has(id)) {
this.depIds.add(id)
this.deps.push(dep)
dep.addSub(this)
}
}
update() {
if (this.lazy) {
this.dirty = true // 计算属性懒更新
} else if (this.sync) {
this.run() // 同步执行
} else {
queueWatcher(this) // 异步批量更新
}
}
}
Watcher通过 get() 触发getter收集依赖,update() 派发更新。
依赖收集过程
JavaScript
// 组件渲染Watcher
mountComponent(vm) {
updateComponent = () => {
vm._update(vm._render())
}
new Watcher(vm, updateComponent, noop)
}
// Watcher.get() 执行时
pushTarget(this) // Dep.target = this.watcher
vm._render() // 执行渲染函数
-> _c('div') // 访问data.msg
-> msg的getter
-> dep.depend() // 收集当前Watcher
渲染函数执行时访问响应式数据,触发getter收集渲染Watcher。
数组响应式处理
JavaScript
const arrayProto = Array.prototype
const arrayMethods = Object.create(arrayProto)
const methodsToPatch = [
'push', 'pop', 'shift', 'unshift',
'splice', 'sort', 'reverse'
]
methodsToPatch.forEach(function(method) {
const original = arrayProto[method]
def(arrayMethods, method, function(...args) {
const result = original.apply(this, args)
const ob = this.__ob__
let inserted
// 新元素观测
switch (method) {
case 'push':
case 'unshift':
inserted = args
break
case 'splice':
inserted = args.slice(2)
break
}
if (inserted) ob.observeArray(inserted)
// 通知更新
ob.dep.notify()
return result
})
})
数组通过拦截7个变异方法,新元素观测和派发更新。
要点总结
defineReactive劫持getter/setter,每个属性独立Dep实例- getter收集
Dep.target指向的当前Watcher,setter派发更新 Dep管理属性订阅的Watcher数组,使用副本遍历防止修改Watcher通过get()触发依赖收集,update()派发更新- 数组通过拦截
push/pop/shift/unshift/splice/sort/reverse实现响应式 - 计算属性使用
lazy标记,更新时只设dirty不立即执行
📝 发现内容有误?点击此处直接编辑