一文讲清楚Vue3中computed的使用

前言

computed() 方法期望接收一个 getter 函数,返回值为一个计算属性 ref。和其他一般的 ref 类似,你可以通过 XXX.value 访问计算结果。计算属性 ref 也会在模板中自动解包,因此在模板表达式中引用时无需添加 .value

基本使用

先看一下以下代码

import {ref, computed } from 'https://cdn.bootcdn.net/ajax/libs/vue/3.3.4/vue.esm-browser.js'

let template = `
    <table>
        <tr>
            <td>单价</td>
            <td>数量</td>
            <td>总额</td>
        </tr>

        <tr>
            <td v-on:click='unit_price++'>{{unit_price}}</td>
            <td v-on:click='quantity++'>{{quantity}}</td>
            <td>{{total_price}}</td>
        </tr>
    </table>
`

export default {
    setup: function () {
        let unit_price = ref(11)
        let quantity = ref(90)
        let total_price = unit_price.value * quantity.value

        return {unit_price, quantity, total_price}
    },
    template
}

如上,我们有两个响应式数据unit_price和quantity,还有一个总价total_price是这两个响应式数据相乘得出来的,在组件挂载时我们能看到total_price的正确结果990,但当我们通过点击unit_price或quantity改变响应式数据时,total_price却未能同步进行更新。

可能你会觉得在模板直接写逻辑不就好了吗,就像

<td>{{unit_price*quantity}}</td>

确实,这样也行,但如果逻辑较多较复杂的情况下,会让模板变得臃肿,难以维护。

因此我们推荐使用计算属性来描述依赖响应式状态的复杂逻辑。

import { ref, computed } from 'https://cdn.bootcdn.net/ajax/libs/vue/3.3.4/vue.esm-browser.js' 

setup: function () {
    let unit_price = ref(11)
    let quantity = ref(90)
    let total_price = computed(() => unit_price.value * quantity.value)

    return {unit_price, quantity, total_price}
}

这样,无论我们改变unit_price还是quantity,total_price都能侦听到变化进行更新。

Vue 的计算属性会自动追踪响应式依赖。它会检测到 total_price 依赖于 unit_pricequantity,所以当二者改变时,任何依赖于 total_price 的绑定都会同时更新。

修改计算属性

其实,像上面那样子直接给computed传一个getter函数只能获取到计算属性的值,要想修改计算属性的值,需要给computed传一个对象,这个对象由一个getter函数和一个setter函数组成,通过setter函数我们就可以修改计算属性了。

<tr>
    <td v-on:click='unit_price++'>{{unit_price}}</td>
    <td v-on:click='quantity++'>{{quantity}}</td>
    <td v-on:click='total_price=0'>{{total_price}}</td>
</tr>

setup: function () {
    let unit_price = ref(11)
    let quantity = ref(90)
    let total_price = computed({
        get: () => unit_price.value * quantity.value,
        set: (newValue) => [unit_price.value, quantity.value] = [newValue, newValue]
    })

    return { unit_price, quantity, total_price }
}

当我们单击页面上总价的数值时,就会给total_price赋值为0,然后继续把0赋值给unit_price和quantity,就达到了结果清零的效果。计算属性返回的值是派生状态,更改它是没有意义的,应该更新它所依赖的源状态以触发新的计算。

使用计算属性的注意事项

  1. 不要改变其他状态、不要在 getter 中做异步请求或者更改 DOM!

  2. 计算属性描述的是如何根据其他值派生一个值。因此 getter 的职责应该仅为计算和返回该值。