响应式数据与依赖追踪
Vue通过 Observer 递归观测对象和数组,Dep 收集依赖,Watcher 观察更新,实现数据驱动视图更新。
Observer 观测器
JavaScript
class Observer {
value: any
dep: Dep
vmCount: number
constructor(value: any) {
this.dep = new Dep()
this.vmCount = 0
def(value, '__ob__', this) // 挂载__ob__引用
if (Array.isArray(value)) {
// 数组: 重写原型方法
if (hasProto) {
protoAugment(value, arrayMethods)
} else {
copyAugment(value, arrayMethods, arrayKeys)
}
this.observeArray(value)
} else {
// 对象: 遍历属性定义响应式
this.walk(value)
}
}
walk(obj: Object) {
const keys = Object.keys(obj)
for (let i = 0; i < keys.length; i++) {
defineReactive(obj, keys[i])
}
}
observeArray(items: Array<any>) {
for (let i = 0, l = items.length; i < l; i++) {
observe(items[i])
}
}
}
Observer是观测入口,对象遍历属性定义响应式,数组重写原型方法。
defineReactive 响应式定义
JavaScript
function defineReactive(obj, key, val, customSetter, shallow) {
const dep = new Dep()
let childOb = !shallow && observe(val)
Object.defineProperty(obj, key, {
get() {
const value = val
if (Dep.target) {
dep.depend() // 收集当前Watcher
if (childOb) {
childOb.dep.depend() // 收集数组/对象Observer
}
}
return value
},
set(newVal) {
if (newVal === val) return
val = newVal
childOb = !shallow && observe(newVal)
dep.notify() // 通知所有Watcher更新
}
})
}
getter收集依赖,setter派发更新,新值递归观测。
Dep依赖管理
JavaScript
class Dep {
static target: ?Watcher = null
id: number
subs: Array<Watcher>
constructor() {
this.id = uid++
this.subs = []
}
depend() {
if (Dep.target) {
Dep.target.addDep(this)
}
}
notify() {
const subs = this.subs.slice()
for (let i = 0; i < subs.length; i++) {
subs[i].update()
}
}
}
每个属性独立Dep,Dep.target 全局指向当前活跃Watcher。
Watcher观察者
JavaScript
class Watcher {
constructor(vm, expOrFn, cb, options) {
this.vm = vm
this.cb = cb
this.deps = []
this.depIds = new Set()
this.getter = parsePath(expOrFn)
this.value = this.get()
}
get() {
pushTarget(this) // 设置Dep.target
value = this.getter.call(this.vm, this.vm)
return value
}
addDep(dep: Dep) {
if (!this.depIds.has(dep.id)) {
this.depIds.add(dep.id)
this.deps.push(dep)
dep.addSub(this)
}
}
update() {
queueWatcher(this) // 异步批量更新
}
}
Watcher通过 get() 触发依赖收集,update() 进入更新队列。
依赖收集流程
JavaScript
// 1. 组件挂载
mountComponent(vm) {
new Watcher(vm, () => {
vm._update(vm._render()) // 渲染函数
})
}
// 2. Watcher.get() 执行
pushTarget(watcher) // Dep.target = watcher
vm._render() // 执行渲染
-> _c('div') // 访问data
-> data.msg的getter
-> dep.depend() // 收集watcher
-> childOb.dep.depend() // 收集数组Observer
// 3. 数据变化
this.msg = 'new'
-> msg的setter
-> dep.notify()
-> watcher.update()
-> queueWatcher(watcher)
渲染时访问数据触发getter收集Watcher,数据变化触发setter派发更新。
要点总结
Observer是观测入口,对象遍历属性、数组重写原型方法defineReactive劫持getter/setter,每个属性独立Dep- getter收集
Dep.target指向的Watcher,setter派发更新 Dep管理属性订阅的Watcher数组,使用副本遍历Watcher通过get()触发依赖收集,update()进入更新队列- 新值递归观测,确保深层数据变化也能触发更新
📝 发现内容有误?点击此处直接编辑