主要是 为了实现不被视图依赖的 属性的dep里面的subs为空,防止这些值改变造成视图重新渲染
看下面的代码
<template>
<div id="app">
<div>
a:{{ a }}
<button @click="click">App Change</button>
<HelloWorld1 v-if="a % 2 === 0" :a="a"> </HelloWorld1>
<HelloWorld2 v-else :a="b"> </HelloWorld2>
</div>
</div>
</template>
上面例子中,a属性的改变是一定会造成视图的重新渲染,但是b就不一定 在a是奇数的时候,子组件HelloWorld2出现在视图中的时候,b会造成App组件的重新渲染 在b是偶数的时候,子组件HelloWorld2不会出现在视图中,也就是说b是不会引起任何视图的改变 如果是上述例子的情况,v-if根据不同的情况显示或者隐藏子组件HelloWorld2。
如果不用newDeps和cleanupDeps去除那些前一次渲染被视图依赖本次渲染不被视图依赖的属性,那么即使在HelloWorld2没有被渲染b属性没有被依赖的情况下,改变b的值依然会造成App组件的重新渲染。
那么Vue是怎么清除这些值的?
在App组件首次渲染的时候(a的初始值为1),a,b两个值会通过definePorperty把这两个值分别和两个dep对象关联起来,假设dep(id=7)关联的是a,dep(id=8)关联的是b,那么在App组件的渲染过程中会触发a,b的dep收集App的渲染watcher,所以首次渲染之后,
dep[id=7].subs = [App渲染watcher]
dep[id=8].subs = [App渲染watcher]
所以首次渲染后,a和b的改变都会通过notify事件通知各自dep的subs数组每一个执行
当点击按钮App Change改变a的值得时候,a变成了偶数,a重新渲染,这次渲染后App 子组件Helloworld2没有渲染出来,不会触发dep.depend,由以下代码可以看出来,dep[id=8]目前只存在渲染watcher的 deps(上次渲染收集的)中,不会出现在newDeps(本次渲染收集的)中,所以第二渲染之后,
这时候App组件的渲染watcher的dep相关的属性如下
deps数组是[dep[id=7],dep[id=8]],
depIds是[7,8],
newdeps是[dep[id=7],
newDepIds是[7]
Watcher.prototype.addDep = function addDep(dep) {
debugger
var id = dep.id;
if (!this.newDepIds.has(id)) {
this.newDepIds.add(id);
this.newDeps.push(dep);
if (!this.depIds.has(id)) {
dep.addSub(this);
}
}
};
App渲染watcher(下面说的watcher指的都是这个watcher)执行结束的最后会执行this.cleanupDeps();方法,
这个方法就是将watcher.deps数组里面所有的dep遍历一遍,如果在newDepIds找不到这个id,则认为本次渲染中这个id对应的dep不会造成这个watcher的更新,会把这个watcher从dep的subs数组中移除,所以dep改变不会再造成watcher的执行,
如上面deps数组是[dep[id=7],dep[id=8]],newDepIds是[7],8没有出现在newDepIds数组中,dep[id=8]会将watcher从他的subs移除
最后把newdeps数组赋值给deps数组,清空newdeps数组
Watcher.prototype.cleanupDeps = function cleanupDeps() {
debuggernewDepIds
var i = this.deps.length;
while (i--) {
var dep = this.deps[i];
if (!this.newDepIds.has(dep.id)) {
dep.removeSub(this); //this是当前watcher,如果这个dep的id没有出现在newDepIds,dep会移除subs里面的对应watcher
}
}
var tmp = this.depIds;
this.depIds = this.newDepIds;
this.newDepIds = tmp;
this.newDepIds.clear();
tmp = this.deps;
this.deps = this.newDeps;
this.newDeps = tmp;
this.newDeps.length = 0;
};