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, foo 是 this._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数组中的值触发了修改)都只会返回缓存中的值
-
测试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, 因为a在deps中, 所以会执行foo函数, 首先清空deps 数组,然后函数执行过程中会碰到this.a的取值,就把a加入到deps数组中, 到函数执行完毕,deps中也只有a, 那么deps的长度从3变为1