vue中利用Object.defineProperty定义getter/setter实现了数据的响应式,里面重要的一点就是依赖收集,它是计算属性和被依赖属性之间的桥梁,有了依赖收集(Dep),当被依赖对象A的getter函数执行时,所有依赖它的东东就会被收集到A的依赖库中,当A的setter执行时,依赖库中的东东就会被一个一个执行,通知依赖对象B。而这些被封装的依赖是在B的getter执行的时候注入到Dep的静态属性target中的
图很丑,包涵
/*
* 定义一个“依赖收集器”
* */
class Dep {
constructor () {
this.deps = []
}
/* 收集依赖 */
depend () {
if (Dep.target && this.deps.indexOf(Dep.target) === -1) {
this.deps.push(Dep.target)
}
}
/* 执行依赖 */
notify () {
this.deps.forEach((dep) => {
dep()
})
}
}
/* target是被观察者的回调执行结果和计算属性被更新后调用的函数的封装 */
Dep.target = null
/* 把一个对象的每一项都转化成可观测对象 */
class Observable {
constructor (obj) {
return this.walk(obj)
}
walk (obj) {
const keys = Object.keys(obj)
keys.forEach((key) => {
this.defineReactive(obj, key, obj[key])
})
return obj
}
/* 使一个对象转化成可观测对象 */
defineReactive (obj, key, val) {
const dep = new Dep()
Object.defineProperty(obj, key, {
get () {
dep.depend()
return val
},
set (newVal) {
val = newVal
dep.notify()
}
})
}
}
/*
* 观察者
* obj: 被观察对象
* key: 被观察者key
* cb: 回调函数,返回“计算属性”的值
* onComputedUpdate: 当计算属性的值被更新时调用*/
class Watcher {
constructor (obj, key, cb, onComputedUpdate) {
this.obj = obj
this.key = key
this.cb = cb
this.onComputedUpdate = onComputedUpdate
return this.defineComputed()
}
defineComputed () {
const self = this
const onDepUpdated = () => {
const val = self.cb()
this.onComputedUpdate(val)
}
Object.defineProperty(self.obj, self.key, {
get () {
Dep.target = onDepUpdated
const val = self.cb()
Dep.target = null
return val
},
set () {
console.error('计算属性无法被赋值!')
}
})
}
}
运行一下:
const hero = new Observable({
health: 3000,
IQ: 150
})
new Watcher(hero, 'type', () => {
return hero.health > 4000 ? '坦克' : '脆皮'
}, (val) => {
console.log(`我的类型是:${val}`)
})
console.log(`英雄初始类型:${hero.type}`)
hero.health = 5000
// -> 英雄初始类型:脆皮
// -> 我的类型是:坦克
参考资料:
https://zhuanlan.zhihu.com/p/29318017