Computed 计算属性
基本使用
- 下方例子中,用户输入 firstName 或 lastName 都会引起 fullName 的重新拼接
选项式 API
<template>
<div>
{
{ fullName }}
<input v-model="firstName"/>
<input v-model="lastName"/>
</div>
</template>
<script>
export default {
data() {
return {
firstName: '',
lastName: ''
}
},
computed: {
fullName() {
return this.firstName + this.lastName
}
}
}
</script>
组合式 Api
<template>
<div>
{
{ fullName }}
<input v-model="firstName"/>
<input v-model="lastName"/>
</div>
</template>
<script setup>
import {computed, ref} from "vue";
const firstName = ref('')
const lastName = ref('')
const fullName = computed(() => firstName.value + lastName.value)
</script>
可变计算属性
- 一般来说,上述的用法,computed 计算出来的结果是不可变的,那么 computed 的另外一种用法则支持其发生修改
- 这种用法在 Vue2 中的组件上使用 v-model 时很好用,一般来说,子组件是不能更改 props 的值的,那么在 vue2 中如果在组件上使用
v-model 来双向绑定一个属性,通常子组件中使用 value 来接收这个值(Vue3 默认使用 modelValue 来接收),通过触发 input
事件来修改这个值(vue3 默认触发 update:modelValue
事件来修改),使得该组件允许双向绑定一个属性。在子组件内部,该值始终是单向数据流,其获取与修改均在父组件中完成,子组件无法直接修改其值,只能通过触发事件来修改父组件的值,此时,使用 computed 的 get 和 set 就可以优化这个操作
<template>
<div>
<input v-model="name"/>
</div>
</template>
<script>
export default {
props: {value: {type: String, default: ''}},
computed: {
name: {
get() {
return this.value
},
set(newValue) {
this.$emit('input', newValue)
}
}
}
}
</script>
- 上方示例中,子组件可以直接修改 name 来触发父组件的更新,也可以通过读取 name 来获取父组件传递下来的值
- 另外一个案例在与后端的交互中,请求参数中要传入一个时间范围,使用 startTime 和 endTime 来接收,而 elementPlus
的时间选择器绑定的是一个数组,此时就可以使用 computed 的 get 和 set 来进行映射
<template>
<div>
<el-date-picker v-model="timeRange"></el-date-picker>
</div>
</template>
<script setup>
import {computed, reactive} from "vue";
const param = reactive({startTime: '', endTime: ''})
const timeRange = computed({
get: () => [param.startTime, param.endTime],
set: nv => {
if (!nv) return
if (nv[0]) param.startTime = nv[0]
if (nv[1]) param.endTime = nv[1]
}
})
</script>
- 使用上述方法,就可以优化 el-date-picker 的使用
Computed 的缓存机制与执行时机问题
- 很多前端开发者都熟读并背诵:‘Computed 计算属性会在其依赖发生变化的时候重新进行计算,并且将结果缓存起来,如果结果不变,那么不会重复执行’
- 对于这段话的理解,很多开发者可能有误,很多人可能存在的误区:
- 计算属性是在其依赖的响应式对象发生变化时重新计算的
- 计算属性的缓存是基于计算结果的
- 官网的解释:
计算属性值会基于其响应式依赖被缓存。一个计算属性仅会在其响应式依赖更新时才重新计算。这意味着只要 author.books 不改变,无论多少次访问 publishedBooksMessage 都会立即返回先前的计算结果,而不用重复执行 getter 函数。
- 官网的解释中可以得知,计算属性的缓存是基于其依赖的响应式变量,而非结果,可以做如下实验:使用一个计算属性计算 count1 和 count2 的值,2 + 3 = 5,如果 computed 的缓存是基于结果的,那么当 count1 和 count2 变成 1 + 4 = 5 时,计算属性应该不会执行,因为结果都是 5
<body>
<div id="app">
<div>{
{ count1 }} + {
{ count2 }} = {
{ computedCount }}</div>
<button @click="countUpdate">更新 count</button>
</div>
</body>
<script>
const {
createApp, computed