看这篇以前,若是没有看过以前的文章,可拉到文章末尾查看以前的文章。vue
在 step2
中,咱们实现了一个管理依赖的 Dep
,可是仅仅使用这个类并不能完成咱们想实现的功能,并且代码的解耦上也有点小问题。如下是在 step2
中最后说的几个问题:git
第一个问题显示出来一个问题,因为咱们的依赖是函数,为了函数的执行咱们只能讲参数传进去,这个问题的根源在于咱们的依赖是一个函数;github
第二个问题其实反映出当前的 dep
实例只有在 defineReactive
中使用,而没有暴露出来,只要在外部有这个实例的引用,那么咱们就能顺利的调用移除依赖了(removeSub
)。数组
解决第一个问题很简单,咱们把某个属性的值、对应值变化时须要执行的函数抽象成一个对象,而后把这个对象当成是依赖,推入依赖管理中。函数
在第一个问题的基础上第二个问题就能解决了,咱们只须要把 dep
的引用保存在依赖对象中就能够了。测试
固然我也是在看了 Vue
源码的基础上才有了上面的解决办法,这里不得不给尤大大赞一个。优化
有了以上的考虑,那个依赖对象在 Vue
中就是 Watcher
。this
let Watcher = function(object, key, callback){ this.obj = object this.getter = key this.cb = callback this.dep = null this.value = undefined this.get = function(){ Dep.target = this let value = this.obj[this.getter] Dep.target = null return value } this.update = function(){ const value = this.obj[this.getter] const oldValue = this.value this.value = value this.cb.call(this.obj, value, oldValue) } this.addDep = function(dep) { this.dep = dep } this.value = this.get() }
上述代码实现了一个 Watcher
,为了方便起见我这里叫它监听。code
该类的实例保存了须要监听的对象(object
),取值方法(key
),对应的回调(callback
),须要监听的值(value
),以及取值函数(get
)和触发函数(update
),这样咱们就把依赖相关的全部内容保存在这个 Watcher
的实例中。对象
为了保存对 Dep
的引用,在 Watcher
中设置了 dep
,用于存放该监听被那个 Dep
给引用了。
因为在 Watcher
实例化的时候,咱们已经对相应的值取了一次值,就是将如下代码放在在 Watcher
中
Dep.target = function(newValue, oldValue){ console.log('我被添加进去了,新的值是:' + newValue) } object.test Dep.target = null
对应的代码为
this.get = function(){ Dep.target = this let vaule = this.obj[this.getter] Dep.target = null return value } this.value = this.get()
因此在编写代码的时候,不用特意的去触发 get
添加依赖。
那么针对 Watcher
咱们须要改造一下以前实现的 Dep
和 defineReactive
函数。
Watcher
因此在 Dep
中 notify
应该改为 Watcher
下的触发函数:update
watcher
中存放了变量的状态,因此不须要在 defineReactive
函数中传入参数let Dep = function(){ this.subs = [] this.addSub = function(sub){ this.subs.push(sub) } this.removeSub = function(sub){ const index = this.subs.indexOf(sub) if (index > -1) { this.subs.splice(index, 1) } } this.notify = function(){ // 修改触发方法 this.subs.forEach(watcher=>watcher.update()) } } Dep.target = null let defineReactive = function(object, key, value){ let dep = new Dep() Object.defineProperty(object, key, { configurable: true, enumerable: true, get: function(){ if(Dep.target){ dep.addSub(Dep.target) // 添加 watcher 对 dep 的引用 Dep.target.addDep(dep) } return value }, set: function(newValue){ if(newValue != value){ value = newValue // 不须要特意传入参数 dep.notify() } } }) }
接下来咱们来测试一下
let object = {} defineReactive(object, 'test', 'test') let watcher = new Watcher(object, 'test', function(newValue, oldValue){ console.log('做为 watcher 添加的第一个函数,很自豪。新值:' + newValue) }) object.test = 'test2' // 做为 watcher 添加的第一个函数,很自豪。新值:test2 let watcher2 = new Watcher(object, 'test', function(newValue, oldValue){ console.log('做为 watcher 添加的第二个函数,也很自豪。新值:' + newValue) }) object.test = 'test3' // 做为 watcher 添加的第一个函数,很自豪。新值:test3 // 做为 watcher 添加的第二个函数,也很自豪。新值:test3 // 接着咱们来试一下删除依赖,把 watcher2 给删除 watcher2.dep.removeSub(watcher2) object.test = 'test4' // 做为 watcher 添加的第一个函数,很自豪。新值:test4
经过上面代码,咱们成功解耦,用一个监听来处理某个属性的内容(oldValue
, newValue
, callback
),并且咱们也可以去除 dep
中没用的依赖。
固然这个 Watcher 仍是须要优化的,好比被多个 Dep
引用,这个就得存一个数组,以后继续优化。