在上一篇中实现了Dep
类,对依赖进行管理,但还遗留了两个问题没解决:
- 解耦不完全,需要传递参数
- 外部无法移除依赖
思考
如何解决第一个问题?
因为我们依赖的是一个函数,为了执行函数,我们只能传入参数。我们可以把某个对象的值、对应值变化时需要执行的函数抽象为一个对象,把这个对象当作依赖。
如何解决第二个问题?
因为dep
实例只能在defineReactive
函数中使用,没有暴露出去,要在外部使用dep
实例中的方法,需要把dep
的引用保存在依赖对象中。
综上所述,需要的这个依赖对象,即为Vue
中的Watcher
Watcher 类的实现
no1. 封装 Watcher
1 | class Watcher { |
no2. 改进 Dep
1 | class Dep { |
no3. 改进 defineReactive
1 | const defineReactive = function(obj, key, val) { |
no4. 使用
1 | // defineReactive 函数的调用 |
首先定义一个对象 obj
,使用defineReactive
函数拦截 name
属性的读写。
再定义一个Dep
类,向外暴露一个 Dep.target
属性,此属性接受要注入的依赖,同时具备addSub
、removeSub
、notify
三个方法管理name
属性的依赖。
最后定义一个Watcher
类,传入 obj
对象、name
属性和依赖函数,并提供get
方法注入依赖,update
方法执行依赖,同时将 Dep
实例挂载到Watcher
实例上,实现外部管理依赖。
三个步骤无间的配合,实现过程实在是巧妙,不得不为尤大大点赞。
虽然我们对依赖的处理进行了完全的解耦,但还是此时的Watcher
类仍然存在问题:
- 我们只是简单的监听了单个属性,但一般视图层的渲染是依赖于多个属性。
- 多个属性可能属于同一个
Watcher
,换句话说多个Dep
依赖于同一个Watcher
,那么Watcher
应该如何保存这些Dep
。
多属性监听及多依赖处理
1 | class Watcher { |
之前在添加依赖时,只是在获取对应属性的值时添加,即下面这段代码:
1 | const val = this.obj[this.getter] |
现在我们将添加依赖的属性改为一个函数,由外部决定哪些属性需要监听,即:
1 | const val = this.getter.call(obj) |
此时外面传入的函数中带有获取name
属性值,我们就可为name
属性添加依赖,比如:
1 | const watcher = new Watcher(ojb, function() { return this.name }, function(newVal, oldVal) => console.log('可添加多属性的依赖') ) |
为了解决多Dep
依赖同一个Watcher
,我们将Watcher
的dep
该为一个数组,并提供一个清除所有依赖的方法:
1 | class Watcher { |
最后附上改进后的调用结果:
1 | const obj = {} |