
computed 做什么
computed 从 getter 函数派生出一个新值,返回值是 Ref。getter 里读取的响应式数据就是它的依赖,依赖变化时重新计算,依赖不变时直接返回缓存结果,不会重复执行。
只有被读取时才会计算,这叫惰性求值。getter 必须是纯函数:只读取响应式数据,不产生副作用。
只读的 getter 写法
只传一个 getter 函数时,返回值是只读 Ref,类型是 Readonly<Ref<Readonly<T>>>,对 .value 赋值会报错。
import { computed, ref } from 'vue'
const price = ref(0)const label = computed<string>(() => '$' + price.value)
label.value // '$0'label.value = '$1' // 只读,赋值会报错模板里会自动解包,直接写 label 就行;在 JS 里要通过 .value 读取。
可写的 get/set 写法
需要反向写入时,传一个带 get 和 set 的对象。get 仍然从依赖派生值,set 负责把写入的新值映射回依赖,这样得到的 computed 可读也可写。
import { computed, ref } from 'vue'
const price = ref(0)
const priceText = computed({ get: () => String(price.value), set: (value: string) => { // 输入框给的是字符串,存进 price 前先转成数字 price.value = Number(value) || 0 },})priceText 可以直接绑到 v-model 上:读取时拿到字符串,写入时转成数字存进 price。set 只负责把写入映射回依赖,可写 computed 只用在双向绑定这类场合。
购物车案例
下面用两个 computed 处理同一份商品列表,一个按关键字过滤,一个汇总总价。
<template> <div> <input v-model="keyWord" placeholder="请输入名称" type="text"> <table style="margin-top: 10px;" width="500" cellspacing="0" cellpadding="0" border> <thead> <tr> <th>物品</th> <th>单价</th> <th>数量</th> <th>总价</th> <th>操作</th> </tr> </thead> <tbody> <tr v-for="(item, index) in searchData"> <td align="center">{{ item.name }}</td> <td align="center">{{ item.price }}</td> <td align="center"> <button @click="item.num > 1 ? item.num-- : null">-</button> <input v-model="item.num" type="number"> <button @click="item.num < 99 ? item.num++ : null">+</button> </td> <td align="center">{{ item.price * item.num }}</td> <td align="center"> <button @click="del(index)">删除</button> </td> </tr> </tbody> <tfoot> <tr> <td colspan="5" align="right"> <span>总价:{{ total }}</span> </td> </tr> </tfoot> </table> </div></template>
<script setup lang="ts">import { computed, reactive, ref } from 'vue'
interface Data { name: string price: number num: number}
const keyWord = ref('')const data = reactive<Data[]>([ { name: '小满的绿围巾', price: 100, num: 1 }, { name: '小满的红衣服', price: 200, num: 1 }, { name: '小满的黑袜子', price: 300, num: 1 },])
const searchData = computed(() => { return data.filter((item) => item.name.includes(keyWord.value))})
const total = computed(() => { return data.reduce((prev: number, next: Data) => { return prev + next.num * next.price }, 0)})
const del = (index: number) => { data.splice(index, 1)}</script>searchData依赖data和keyWord,返回过滤后的列表total只依赖data,把每行的单价乘数量累加
修改某行的数量时 total 重算;删除某一行时两个 computed 都失效;都等到下次读取时才计算,表格跟着更新。
什么时候用 computed
computed 有缓存,依赖不变时不会重算;在模板里直接调用函数则每次渲染都会重新执行。由响应式数据算出来的派生、过滤、聚合结果适合放 computed。
异步请求、DOM 操作、修改其他状态都是副作用,放在事件处理函数或 watch 里,不要写进 getter。
Vue 3 学习笔记
连载中分享文章
生成精美分享图或复制链接,与更多人分享本文。
继续阅读
换条路线
从其他文章中稳定抽取
最后更新于 ,距今已过 214 天
部分内容可能已过时