计算属性的定义
在 Vue3 的 HTML 模板中是支持 JavaScript 表达式的,例如:
<h2>买5个共计:{{ price * 5 }} 元</h2>
但是如果当表达式过于复杂时,模板代码就会变得非常臃肿并且可读性就会变差,恰巧,如果页面中有多个地方需要使用这个表达式,那么整体的 Vue 模板的代码将会变得非常难以维护,而且在 MVVP 模式下,模板中过多的处理数据计算,也会导致模板和控制器之间高度耦合。
Vue.js 中使用了计算属性来解决表达式逻辑过于复杂的场景,使页面尽可能没有数据复杂处理。
计算属性用于从现有数据派生衍生数据,它们基于Vue实例中已有的数据,通过对这些数据进行计算得出新的值,因此我们只需要声明衍生数据的计算逻辑即可,同时计算属性会自动跟踪依赖的数据,并在相关数据发生变化时自动更新计算结果,这样,当依赖数据发生改变时,计算属性会自动重新计算,不需要手动控制何时进行计算或更新,而 JavaScript 表达式则需要我们在特定的时机通过代码执行来计算值。具体代码如下:
<script lang="ts" setup>
import { computed, ref } from 'vue';
const price = ref<number>(6)
const totalPrice = computed(():number => {
return price.value * 5
})
</script>
<template>
<h2>单价:{{ price }} 元/件</h2>
<h2>买5个共计:{{ totalPrice }} 元【计算属性】</h2>
<h2>买5个共计:{{ price * 5 }} 元【JavaScript 表达式】</h2>
</template>
上述代码中,我们使用 ref
创建了一个表示商品单价的 price
数据引用,指定其类型为 number
,初始值为 6
。
接下来,我们使用 computed
创建了一个计算属性 totalPrice
。在计算属性的回调函数中,我们将 price.value
乘以 5
,并返回计算结果。
在模板部分,我们分别使用了计算属性 totalPrice
和 JavaScript 表达式来显示计算结果。其中,由于计算属性会自动跟踪依赖的数据,如果 price
变量被修改,则对应的 totalPrice
的结果也会被立刻修改。
计算属性的使用
在 Vue 3 的 <script setup>
语法糖中,计算属性有两种用法:
-
第一种为上述样例中直接将实现函数以参数的形式传递给
computed()
方法实现,样例如下:<script lang="ts" setup> import { computed } from 'vue'; // 定义计算属性 const propertyName = computed(() => { // 计算并返回属性值 return /* 衍生属性的计算逻辑 */; }); </script> <template> <p>{{ propertyName }}</p> </template>
可以在模板中直接使用双大括号
{{ }}
来显示计算属性的值。 -
第二种则是分别具体实现 getter 和 setter方法,然后再封装成对象传递给
computed()
,样例如下:<script setup lang="ts"> import { computed } from 'vue'; // 定义计算属性 const propertyName = computed({ get: /* Getter 方法 */, set: /* Setter 方法 */ }); </script> <template> <p>{{ propertyName }}</p> </template>
可以通过定义
get
和set
方法来创建带有读取和设置功能的计算属性。get
方法用于获取计算属性的值,而set
方法用于更新计算属性的值。
在计算属性的两种实现方法中,第一种实现方法的底层逻辑仅仅实现了计算属性默认的 getter方法,如果我们需要在 setter 中进行其他数据操作,可以根据具体需求按照第二种写法来实现计算属性。
计算属性的缓存
在 Vue 3 的 <script setup>
语法糖中,计算属性默认是具有缓存的。它们会根据依赖的响应式数据进行缓存,首次渲染时,计算属性会进行初始计算,并将结果缓存起来。在后续渲染中,只有当依赖项发生变化时,计算属性才会重新计算,并更新模板中对应的值。
为了看到的效果更加明显,我们可以直接在表达式中调用 methods 方法修改数据进行对比。样例如下:
<script lang="ts" setup>
import { computed, ref } from 'vue';
const price = ref<number>(6)
// 定义计算属性
const computedPrice = computed(():number => {
console.log("computedPrice 被调用");
return price.value * 5
})
// 定义方法
const methodsPrice = ():number => {
console.log("methodsPrice 被调用");
return price.value * 5
}
</script>
<template>
<h2>单价:{{ price }} 元/件</h2>
<h2>买5个共计:{{ computedPrice }} 元</h2>
<h2>买5个共计:{{ computedPrice }} 元</h2>
<h2>买5个共计:{{ methodsPrice() }} 元</h2>
<h2>买5个共计:{{ methodsPrice() }} 元</h2>
</template>
在模板中分别调用 computedPrice
计算属性两次、 methodsPrice()
方法两次。
我们可以在浏览器的控制台中查看打印的日志:
可以看到右侧的控制台中,计算属性函数只被调用了一次,而方法函数则被调用了两次,原因就是计算属性默认具有缓存功能,在依赖项未发生变化时会直接使用缓存立刻返回结果,而不是再次执行计算返回结果,即在多次访问同一个计算属性时,只会进行一次计算,提高性能。
有了计算属性提供的缓存,可以极大的提高页面渲染效率。但是如果计算属性函数中的数据不是响应式依赖,就不会触发计算属性的缓存机制。