主要做的事情是:
在自身实例化时往订阅器(dep)里面添加自己。
自身必须有一个update()方法 。
待属性变动dep.notice()通知时,能调用自身的update()方法,并触发Compile中绑定的回调。
先给出全部代码,再分析具体的功能。
//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。
实现VMVM
MVVM作为数据绑定的入口,整合Observer、Compile和Watcher三者,通过Observer来监听自己的model数据变化,通过Compile来解析编译模板指令,最终利用Watcher搭起Observer和Compile之间的通信桥梁,达到数据变化 -> 视图更新;视图交互变化(input) -> 数据model变更的双向绑定效果。
function MVVM(options) { this.$options = options || {}; var data = this._data = this.$options.data; var _this = this; // 数据代理 // 实现 vm._data.xxx -> vm.xxx Object.keys(data).forEach(function(key) { _this._proxyData(key); }); observe(data, this); this.$compile = new Compile(options.el || document.body, this); }
内容版权声明:除非注明,否则皆为本站原创文章。