Skip to content

避免无用更新

javascript
// 为什么 render watcher 要和 dep 互相收集?
// 为了清理 dep 收集的某些 render watcher,使得 dep 改变后不会触发这些
// render watcher的更新。this.get() 最后会调用 cleanupDeps 用于清理 watcher,
// 也就是每次页面更新后都会清理 watcher,cleanupDeps 遍历 this.deps 每一项,
// 如果某一项的 id 不在 newDepIds 中,就调用 dep.removeSub(this) 将 dep.subs
// 中收集的这个 watcher 移除。由于 watcher 中收集了 dep,才能对该 dep 调用
// dep.removeSub(this) 从而在 dep.subs 中把自己移除。
cleanupDeps() {
  let i = this.deps.length
  while (i--) {
    const dep = this.deps[i]
    // 如果旧的 dep.id 在 newDepIds 中不存在,dep 中移除这个 watcher
    if (!this.newDepIds.has(dep.id)) {
      dep.removeSub(this) // Array.prototype.splice
    }
  }
  // depIds 和 newDepIds 互换
  let tmp: any = this.depIds
  this.depIds = this.newDepIds
  this.newDepIds = tmp
  // 清除旧的 depIds
  this.newDepIds.clear()
  // deps 和 newDeps 交换
  tmp = this.deps
  this.deps = this.newDeps
  this.newDeps = tmp
  // 清除旧的 deps
  this.newDeps.length = 0
}
javascript
// 初始化时 render watcher 订阅了 foo 的 dep,然后我们执行了 changeFlag 方法把
// flag 置为 false。如果不清理 render watcher,调用 change 的时,foo 的 dep 会通知
// render watcher 更新,这显然不合理。所以第一次 render watcher 的 旧 deps 包含
// foo 的 dep,调用 changeFlag 更新后通过对比新旧 deps,发现并没有 foo 的 dep,
// 所以清理掉 foo 的 dep 中的这个 render watcher,当我们再执行 change 时视图旧不会
// 更新了,避免无用的更新。
<template>
  <div>
    <div v-if="flag">{{ foo }}</div>
  </div>
</template>

<script>
export default {
  data(){
    return {
      flag: true,
      foo: 'foo'
    }
  },
  
  methods: {
    changeFlag () {
      this.flag = false
    },

    change () {
      this.foo = 'bar'
    }
  },
}
</script>