为什么比起 watch, computed 的性能更好?
我个人认为最主要的原因在于更新的时机.
watch
众所周知, 响应式数据有一个收集依赖和触发事件的过程. 当依赖是其它响应式数据的事件时, 那么那个响应式数据变化时, 这个数据也需要改变.
举个例子, 其中一个依赖是这样的:
js
effect(() => {
B.value // 不知道干嘛, 反正是个get操作, 收集起来
B.value = A.value // 这里get了A
})
这样子 A 收集到了这个依赖, B 也会收集到这个依赖. 当 A 改变时, 就会重新执行这个函数, 这样就会改变 B 了. 也就是说, 这里 A 改变, B 就立刻改变.
对于 watch 来说是这样的, 比如
js
watch(
() => A.value,
(newV) => {
B.value = newV
}
)
运行到这个 watch 函数时, 如果第一个参数是函数那就执行, 这是一个收集依赖的过程; 如果是响应式数据就执行一个 getter. 当 A.value 改变时, 立刻执行第二个参数.
可以试一下下面这个代码
js
import { ref, watch } from 'vue'
const a = ref(0)
watch(() => {
console.log('收集依赖!')
return a.value
}, () => {
console.log('改变了!')
})
setInterval(() => {
a.value++
}, 500)
没意外的话每500ms就会打印一次改变了.
computed
computed使用的是一种懒响应, 在第一次被调用的时候才会收集响应, 然后将最后的值缓存起来. computed类中有一个判断缓存是否有效的布尔值, isDirty. 之后被调用时, 先判断缓存是否有效, 如果有效, 那就不执行之前用来取值的依赖了, 而是把缓存的值交出去; 如果无效, 那就调用之前的依赖, 更新缓存再把缓存交出去.
js
import { computed, ref } from 'vue'
const a = ref(0)
const b = computed(() => {
console.log('改变了!')
return a.value
})
setInterval(() => {
a.value++
}, 500)
没有意外的话什么都不会打印. 因为这里没有调用过b, 因此b也不会去执行取值的依赖. 只有当调用b的时候, b才会取值.
js
import { computed, ref } from 'vue'
const a = ref(0)
const b = computed(() => {
console.log('改变了!')
return a.value
})
setInterval(() => {
a.value++
}, 500)
setTimeout(() => {
console.log(b.value)
}, 2000)
没意外的话大约2秒后会打印出改变了, 并打印出b的值, 可能是4也可能不是4.