使用
computed
被推荐用来描述依赖响应式状态的复杂逻辑。
简单点来说,就是当依赖的状态发生变化,计算属性就会自动更新,并把更新传递出去.
看一个例子:
<script setup>
import { reactive, computed } from 'vue'
const author = reactive({
name: 'John Doe',
books: [
'Vue 2 - Advanced Guide',
'Vue 3 - Basic Guide',
'Vue 4 - The Mystery'
]
})
// 一个计算属性 ref
const publishedBooksMessage = computed(() => {
return author.books.length > 0 ? 'Yes' : 'No'
})
function reduce(){
author.books.pop()
}
function add(){
author.books.push("test")
}
</script>
<template>
<button @click="reduce">
减少
</button>
<button @click="add">
增加
</button>
<p>出版过书籍:</p>
<span>{{ publishedBooksMessage }}</span>
<br>
<p>书籍:</p>
<p v-for="item in author.books">{{item}}</p>
</template>
在线演示
在修改了author.books
里面的数据之后,计算属性会自动“监听”到变化,并通过逻辑运算出结果,用到这个属性的地方,也会同步更新。computed()方法不仅可以接收一个getter,还可以接收一个setter。
官方解释:
computed()
方法期望接收一个getter
函数,返回值为一个计算属性ref
。和其他一般的ref
类似,可以通过publishedBooksMessage.value
访问计算结果。计算属性ref
也会在模板中自动解包,因此在模板表达式中引用时无需添加.value
。
Vue 的计算属性会自动追踪响应式依赖。它会检测到publishedBooksMessage
依赖于author.books
,所以当author.books
改变时,任何依赖于publishedBooksMessage
的绑定都会同时更新。
计算属性缓存 VS 方法
计算属性是基于依赖的,它的值会被缓存,当它依赖的状态发生变化时,才会重新执行getter函数来计算它的值。而方法在每次调用时都要重新跑一边。
computed()的参数
实际上,computed()
通常情况下,只用传入一个函数,这个函数会被当做getter
对待,computed()
返回的结果具有响应性,因为返回的是计算属性ref。computed()
还可以传入setter
,用于某种特殊的场景,写法如下:
const re = computed({
set(val){
// ...
},
get(){
//return ....
}
})
注意事项
计算函数不应有副作用
计算属性的计算函数应只做计算而没有任何其他的副作用,这一点非常重要,请务必牢记。举个例子,不要在计算函数中做异步请求或者更改 DOM!一个计算属性的声明中描述的是如何根据其他值派生一个值。因此计算函数的职责应该仅为计算和返回该值。在之后的指引中我们会讨论如何使用监听器根据其他响应式状态的变更来创建副作用。
避免直接修改计算属性值
从计算属性返回的值是派生状态。可以把它看作是一个“临时快照”,每当源状态发生变化时,就会创建一个新的快照。更改快照是没有意义的,因此计算属性的返回值应该被视为只读的,并且永远不应该被更改——应该更新它所依赖的源状态以触发新的计算。
计算属性无法追踪非响应式依赖
例如想监听页面的宽度变化,直接在computed()
中使用document.querySelector("body").offsetWidth
,是无法达到预期的,因为获取到的值不是响应式的,只是一个数字。想实时监听页面宽度,可以使用window.onresize
的回调,或者使用其他方法(例如定时器,但是要考虑页面的性能)。
在这个例子中,在模板中使用调用方法,也不能实现实时监听宽度变化,因为模板中的方法只在渲染的时候调用一次,随后dom不再重新渲染的话,就获取不到最新的宽度。可以考虑使用nextTick()
或者其他方法,有思路了再来记录。