接着来看一种最典型的watcher行为,在/src/core/instance/lifecycle.js
中的moundComponent
方法中,可以看到一个实例化watcher
的方法
new Watcher(vm, updateComponent, noop, {
before () {
if (vm._isMounted && !vm._isDestroyed) {
callHook(vm, ‘beforeUpdate’)
}
}
}, true /* isRenderWatcher */)
可以看到,他将updateComponent
(可以抽象为渲染行为)传给Watcher
,而在Watcher
的实例化中,将会执行此方法,当然在执行之前,pushTarget(this)
,将这个watcher挂载到公共变量上而后开始执行渲染行为,
class Watch {
constructor(…) {
…
if (typeof expOrFn === ‘function’) {
this.getter = expOrFn
}
this.get();
}
get() {
pushTarget(this) // 挂载行为至公共Target
value = this.getter.call(vm, vm) // 开始执行行为,之所以会有返回值是为了computed服务
popTarget() // 取消挂载,避免下次读取变量时又会绑定此行为
}
}
此时,如果此行为读取了某个响应式变量,那么该变量的getter
将会存储公共变量target
,当行为完成后就会取消行为的挂载,这个时候我们再回过头来看前面的defineReactive
的逻辑
function defineReactive(obj, key) {
const dep = new Dep(); // 每个数据都有一个自己的存储列表
const getter = property && property.get
const setter = property && property.set
Object.defineProperty(obj, key, {
enumerable: true,
configurable: true,
get: function reactiveGetter () {
const value = getter ? getter.call(obj) : val
if (Dep.target) { // 判断公共变量中是否挂载了行为(watcher)
dep.depend() // 将行为(watcher)加入dep(即此变量的存储行为列表)
…
}
return value
},
set: function reactiveSetter(newVal) {
const value = getter ? getter.call(obj) : val
if (newVal === value || (newVal !== newVal && value !== value)) {
return // 判断变量没有变化,则直接返回(后两者判断则是因为NaN!==NaN的特性)
}
if (setter) {
setter.call(obj, newVal) // 开始
} else {
val = newVal
}
dep.notify() // 通知自己这个数据的存储列表,数据发生改变,需要重新执行行为(watcher)
}
});
}
这个时候就很清晰明了了,这就是很多博客文章所说的依赖收集,变量在get时通过公共变量Target
收集依赖(也就是本文所说的行为),在set
时,即变量数据发生改变时,触发更新notify
;
2.2.2 Computed
前文有大致介绍computed
的实现,实际上在介绍完Wacher之后就可以来详细介绍了,计算属性computed
并没有实际的变量,他通过原型链覆盖创造了一个变量指向(src/core/instance/state.js
的initComputed
),回忆一下computed的两种写法
‘fullName’: function() {
return this.firstName + this.secondeName;
}
‘fullName’: {
get: function () {…},
set: function() {…},
}
我们再来看一下initComputed
function initComputed (vm: