vue将数据变的可观测了以后,我们就知道了数据什么时候进行了改变,当数据改变的时候就去更新视图,但是去更新哪个视图呢,如果改变了一个数据,就去更新整个视图,明显这样是不合理的。
最正确的方法就是——哪个视图用了这个变化的数据,哪个视图就进行更新。
哪个视图用到了这个数据,也可以解读为哪个视图依赖了这个数据。
那么,谁用了这个数据谁就是依赖。
在vue中,为每个数据创建了一个数组来存放依赖。谁用到了这个数据,就将谁存放 到这个数组里(这就是依赖收集),当数据变化时,通知数组中所有的依赖进行更新。
在数据观测中,我们可以观测到数据的读取和修改。而数据的变化对应依赖的变化,所以对于依赖的收集我们可以总结为:在getter中收集依赖,在setter中通知依赖更新。
function defineReactive (obj,key,val) {
if (arguments.length === 2) {
val = obj[key]
}
if(typeof val === 'object'){
new Observer(val)
}
const dep = new Dep() // 依赖管理器
Object.defineProperty(obj, key, {
enumerable: true,
configurable: true,
get(){
dep.depend()// 在getter中收集依赖
return val;
},
set(newVal){
if(val === newVal){
return
}
val = newVal;
dep.notify()// 在setter中通知依赖更新
}
})
}
对于以上代码,我们可以看到,在get中通过dep.depend()
来收集依赖,使用dep.notify()
来更新依赖。那么,什么是dep
呢?
依赖管理器主要实现的功能
1、添加一个依赖
2、删除一个依赖
3、通知所有依赖更新
export default class Dep {
constructor () {
this.subs = []
}
addSub (sub) {
this.subs.push(sub)
}
// 删除一个依赖
removeSub (sub) {
remove(this.subs, sub)
}
// 添加一个依赖
depend () {
if (window.target) {
this.addSub(window.target)
}
}
// 通知所有依赖更新
notify () {
const subs = this.subs.slice()
for (let i = 0, l = subs.length; i < l; i++) {
subs[i].update()
}
}
}
export function remove (arr, item) {
if (arr.length) {
const index = arr.indexOf(item)
if (index > -1) {
return arr.splice(index, 1)
}
}
}
在vue中还实现了一个watcher类,为什么把watcher类放在依赖这里讲呢?
回到标题的问题:什么是依赖?
答:谁用到了数据(发生变化),谁就是依赖,我们就为谁创建一个watcher实例。所以说,Watcher
类的实例就可以称为依赖。
export default class Watcher {
constructor (vm,expOrFn,cb) {
this.vm = vm;
this.cb = cb;
this.getter = parsePath(expOrFn)
this.value = this.get()
}
get () {
window.target = this;
const vm = this.vm
let value = this.getter.call(vm, vm)
window.target = undefined;
return value
}
update () {
const oldValue = this.value
this.value = this.get()
this.cb.call(this.vm, this.value, oldValue)
}
}
简单分析一下逻辑:
1、watcher实例化时先执行其构造函数,调用了this.get()
方法
2、get()方法中,首先把实例自身赋给了全局对象window.target
,然后通过this.getter.call
获取一下被依赖的数据,目的是触发数据上面的getter()方法。getter方法会进行依赖收集,执行dep.depend()
。而dep.depend()
会取window.target
上的值放入依赖数组。
3、数据变化时,触发数据的setter,并执行了dep.notify()
,在dep.notify()
遍历所有依赖(也就是watcher实例),并执行实例中的update()
方法,更新视图。
更多干货,我们下篇见!