基础示例
<template>
<p>Has published books:</p>
<span>{{ publishedBooksMessage }}</span>
</template>
<script setup>
import { ref, computed } from 'vue'
const author = ref({
name: 'John Doe',
books: [
'Vue 2 - Advanced Guide',
'Vue 3 - Basic Guide',
'Vue 4 - The Mystery'
]
})
// 一个计算属性
const publishedBooksMessage = computed(() => {
return author.value.books.length > 0 ? 'Yes' : 'No'
})
</script>
你可以简单的把 computed 的返回值,认为是一个通过 ref() 声明的响应式数据,初始值就是传入 computed 的第一个参数的返回值。
import { ref, computed } from 'vue'
const author = ref({
name: 'John Doe',
books: [
'Vue 2 - Advanced Guide',
'Vue 3 - Basic Guide',
'Vue 4 - The Mystery'
]
})
// 类似于计算属性,只不过 author 的值改变时,这个 author2 的值不会因为 author 发生改变而改变
function initValue() {
return author.value.books.length > 0 ? 'Yes' : 'No'
}
const author2 = ref(initValue()) // 可以传递一个初始化函数
// 一个计算属性,如果 author 发生改变会重新计算
const publishedBooksMessage = computed(() => {
return author.value.books.length > 0 ? 'Yes' : 'No'
})
当我们需要在其他函数中使用这个计算属性的时候,也是访问它的 value 属性
// ... 就像上面一样声明了一个 publishedBooksMessage 计算属性
function foo() {
return publishedBooksMessage.value // 返回 'Yes' 或者 'No'
}
多个依赖项
在上面的例子中,计算属性只依赖于 author 这一个响应式数据。实际业务中可能一个计算属性会依赖多个响应式数据。
<template>
{{ computedValue }}
</template>
<script setup>
import { ref } from 'vue'
const baz = ref(false)
const foo = ref(true)
const books = ref([])
const computedValue = computed(() => {
if (baz.value && foo.value && books.value.length) {
return 'Yes'
}
return 'No'
})
</script>
计算属性缓存 vs 方法
方法
在使用双大括号语法绑定模板数据,以及通过 v-bind 绑定 Attribute 的时候,我们可以传递一个函数调用
<template>
<p>{{ getValue() }}</p>
<!-- <p>{{ getValue }}</p> 这样是把整个函数作为纯文本渲染出来,而不是函数返回值. -->
</template>
<script>
import { ref } from 'vue'
const msg = ref('Hello World')
function getValue() {
return msg.value
}
// 别忘了返回出去,除非你用的是 <script setup><\/script>
// 知道我为啥用 \ 符号转义script前面的 / 吗?
// 浏览器解析 js 脚本从 <script> 标签开始,只要碰到 </script> 就认为解析完成了,所以不管在任何地方,不论是 console.log 里,还是注释里,只要不是真正的完成 js 代码编写,都不要出现 </script>。更多知识可以去看 《JavaScript高级程序设计》第四版,在 script 标签那部分有讲到
// 再说一句 尽管我们转义了,打印出来的内容是没有转义符号的,这个请放心 console.log('<\/script>')
return {
getValue
}
</script>
这样,我们就通过方法的形式渲染了 msg ,有同学会关心,我每次去改 msg 的值,页面上会更新吗?
大家可以打开控制台,或者 console,可以看到每次编辑输入框的内容,组件都会更新,模板上的方法都会重新调用。基础/响应式基础-ref篇 有讲DOM更新时机
然后我们再多加入一点内容
现在,我们不去修改msg了,我们去修改count的值。神奇的事情发生了,控制台一直在打印 getValue方法被调用了 ,这就有点让人不爽了,我修改count为啥去调用 getValue ,getValue 内部只和 msg 有关系,并没有使用 count。
这个时候计算属性就能完美解决这个问题,按照文档的说法:计算属性会缓存上一次的计算结果,只在内部涉及到的的响应式数据变化的时候,才会重新计算返回值。
在组件首次被渲染的时候没有缓存,可以看到控制台会打印一次。
得益于Vue的虚拟DOM,我们在某一次修改count后,msg相关的DOM没有变化,这些DOM会被重用。虽然我们的 onUpdated 生命周期函数被执行了,但不代表整个组件都会重新渲染,这也起到了一些性能优化的效果。
我们修改代码为:
这样每次修改 count,都不会引起 getValue 重新计算了。