我们前面讲过,Observe()函数实现data对象的属性劫持,并在属性值改变时触发订阅器的notify()通知订阅者Watcher,订阅者就会调用自身的update方法实现视图更新。
Compile()函数负责解析模板,初始化页面,并且为每个data属性新增一个监听数据的订阅者(new Watcher)。
Watcher订阅者作为Observer和Compile之间通信的桥梁,所以我们可以大致知道Watcher的作用是什么。
主要做的事情是:
先给出全部代码,再分析具体的功能。
//Watcher function Watcher(vm, exp, cb) { this.vm = vm; this.cb = cb; this.exp = exp; this.value = this.get();//初始化时将自己添加进订阅器 }; Watcher.prototype = { update: function(){ this.run(); }, run: function(){ const value = this.vm[this.exp]; //console.log('me:'+value); if (value != this.value){ this.value = value; this.cb.call(this.vm,value); } }, get: function() { Dep.target = this; // 缓存自己 var value = this.vm[this.exp] // 访问自己,执行defineProperty里的get函数 Dep.target = null; // 释放自己 return value; } } //这里列出Observe和Dep,方便理解 Observe.prototype = { defineReactive: function(data,key,value){ let dep = new Dep(); Object.defineProperty(data,key,{ enumerable: true,//可枚举 configurable: false,//不能再define get: function(){ console.log('你访问了' + key); //说明这是实例化Watcher时引起的,则添加进订阅器 if (Dep.target){ //console.log('访问了Dep.target'); dep.addSub(Dep.target); } return value; }, }) } } Dep.prototype = { addSub: function(sub){this.subs.push(sub); }, }
我们知道在Observe()函数执行时,我们为每个属性都添加了一个订阅器dep,而这个dep被闭包在属性的get/set函数内。所以,我们可以在实例化Watcher时调用this.get()函数访问data.name属性,这会触发defineProperty()函数内的get函数,get
方法执行的时候,就会在属性的订阅器dep
添加当前watcher实例,从而在属性值有变化的时候,watcher实例就能收到更新通知。
那么Watcher()函数中的get()函数内Dep.taeger = this又有什么特殊的含义呢?我们希望的是在实例化Watcher时将相应的Watcher实例添加一次进dep订阅器即可,而不希望在以后每次访问data.name属性时都加入一次dep订阅器。所以我们在实例化执行this.get()函数时用Dep.target = this来标识当前Watcher实例,当添加进dep订阅器后设置Dep.target=null。