vue的数据侦听流程图:
先放一张自己画的图,在继续向下看;(自己画的将就看看)
在vue2中,对于一个数据要被侦听,则是通过Object.definedProperty来实现,这个估计大家都知道。那么具体概述就是:
在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() //实例化一个依赖管理器,生成一个依赖管理数组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中通知依赖更新
}
})
}
这里面的defineReactive是把一个对象转化为‘可侦听’;
其中的Dep是一个依赖收集器。对应于图上的Dep;这个Dep存储的就是这个数据的全部依赖。
Dep类里面主要的是depend方法;依赖器里面收集的是一个全局对象,后面会介绍;
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()
}
}
}
谁用到了数据,谁就是依赖,我们就为谁创建一个Watcher实例
这句话的具体含义可以用下面这个例子来说明:
computed(){
Hello(){
return this.hello+' '+this.word;
}
}
这个里面的this.hello和this.word都被Hello这个对象所依赖,所以在this.hello和this.word的dep里面会存有Hello()这个对象;
当computed初始化Hello对象时,会调用watcher这个类;
这个类中的parsePath方法会返回一个遍历真实数据的方法,方便后面使用call方法执行;
如下所示:
export default class Watcher {
constructor (vm,expOrFn,cb) {
this.vm = vm;//我的理解是为一个控制视图的对象;(viewModel)
this.cb = cb;//一个控制视图的回调函数;
this.getter = parsePath(expOrFn)//取出真实的data,比如上面的this.hello,调用parsePath后,剩下的是一个hello的值;
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)
}
}
//parsePath
const bailRE = /[^\w.$]/
export function parsePath (path) {
if (bailRE.test(path)) {
return
}
const segments = path.split('.')
return function (obj) {
for (let i = 0; i < segments.length; i++) {
if (!obj) return
obj = obj[segments[i]]
}
return obj
}
}
调用这个类时,这个类的this针会指向新创建的那个对象,所以里面的window.target=this,是把自己设置到一个全局对象target中,所以图上window.target和watcher是对应的关系,而在依赖收集器中收集的也是这个target;
可以看到在get()函数中调用了this.getter.call(vm,vm),这个函数的执行就是上面执行parsePath方法返回的一个函数,这个函数的意思可以理解为获取Hello中的所有依赖的数据,如例子中的this.hello和this.word的get方法,从而在Dep类的depend中收集刚才的target对象;
所以这时候,上面的this.hello这个数据的依赖器里面就保存了一个Hello的依赖;
最后需要把window.target释放掉;
update函数中主要做的就是把视图更新的操作,这里就不再继续说明;
在返回去看第一张图,思路就会清晰许多;
本文仅仅是我个人理解,如果有错误,希望各位dalao指点;