computed 与watch 的区别

2,595 阅读3分钟

computed 计算属性是基于它们的响应式依赖进行缓存的, 相比之下,每当触发重新渲染时,调用方法 watch 将总会再次执行函数。

那么响应式依赖是什么?

  • 响应式 是指在 data 中声明过的属性
  • 依赖 是指 deps 数组项,deps 数组 是 vue运行时动态修改

响应式

下面的计算属性将不再更新,因为 Date.now() 不是响应式

computed: {
  now: function () {
    return Date.now()
  }
}

依赖

vue 官网有个例子

var vm = new Vue({
 el: '#example',
 data: {
   message: 'Hello'
 },
 computed: {
   // 计算属性的 getter
   reversedMessage: function () {
     // `this` 指向 vm 实例
     return this.message.split('').reverse().join('')
   }
 }
})

Vue 知道 vm.reversedMessage 依赖于 vm.message

这样的说法其实会让人误解, 认为所有写在 reversedMessage 函数中的变量都是依赖, 其实不然, computed 的依赖是运行时决定的 , 而不是在编译时

举个例子:

data(){
    a: 1,
    b: 1,
    c: 1,
  },
  computed: {
    foo() {
      if (this.a >= 0) {
       return this.a;
      } else {
       return this.b + this.c;
      }
     
    },
  },

从源码中可知道, 对于计算属性 foo, foothis._computedWatchers 创建的实例, foo 的依赖放在了 this._computedWatchers.foo.deps 数组里, 只需要打印 this._computedWatchers.foo.deps 并观察 deps 数组的变化,就能知道依赖都是些什么

不可直接打印 this._computedWatchers.foo.deps

直接打印 this._computedWatchers.foo.deps与打印 this._computedWatchers.foo 后手动在控制台展开看到的 deps 并不一致, 原因我也不知道, console 有时候确实是有异步的问题, 希望知道 console 发疯 原因的大佬能指点一下**

但是 deps是封装过的, 直接 console.log 是看不出 deps 数组每项是什么的,需要转变思路,通过观察 deps 数组的长度 来间接认识到依赖项是运行时动态变化的

原理:

foo 运行时,会先清空依赖数组 deps ,然后把执行过程中遇到的所有取值操作 this.a 涉及的属性 a 都加入依赖数组 dep 中, 当依赖数组中的值发生了修改,则会重新执行foo函数, 获取最新的计算结果,把计算结果缓存起来并覆盖上一次的缓存结果。其他情况(非deps数组中的值触发了修改)都只会返回缓存中的值

  • jsbin在线测试

  • 测试1: 在 template 中取{{this.foo}},以便执行一次 foo函数 , 之后查看首次执行 foo函数 之后, deps 依赖中有几项, 可以看到控制台打印出来的 deps 的长度为1, 此时 this.foo 只依赖a,

意味着只有使用了this.a 重新赋值a 并且跟旧 a 不一样, 才会重新执行foo函数 , 其他情况( this.b = 2 ,状态更新,触发 template 重新渲染,遇到 {{this.foo}},本应执行 foo函数, 但实际不会执行 foo函数 , 而是直接返回上一次的缓存结果

  • 测试2: 把 data中的 a 改为-1 , 在template中取{{this.foo}},以便执行一次foo函数, 之后查看首次执行foo函数之后, deps依赖中有几项, 因为走的是else 分支依次触发了this.a, this.b, this.c , 所以可以看到控制台打印出来的 deps的长度为3, 此时this.foo 的依赖a,b,c ,再次修改 this.a = 2 , 因为adeps 中, 所以会执行foo函数, 首先 清空deps 数组,然后函数执行过程中会碰到 this.a 的取值,就把 a 加入到 deps 数组中, 到函数执行完毕, deps 中也只有a, 那么 deps的长度从3变为1

参考: 如何理解vue的computed?