addDep和cleanupDeps中的newDeps有什么作用

1,137 阅读3分钟

主要是 为了实现不被视图依赖的 属性的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。

如果不用newDepscleanupDeps去除那些前一次渲染被视图依赖本次渲染不被视图依赖的属性,那么即使在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;
};