Vue3里computed咋用?这些场景和细节你搞懂没?
不管是刚学Vue3的新手,还是想优化项目代码的老开发,“计算属性(computed)”都是绕不开的核心知识点,但你真的搞懂它啥时候用、咋用更高效了吗?今天咱从基础到实战,把computed的用法、区别、陷阱全拆明白~
computed基础用法是啥?选项式和组合式API写法有啥不同?
Vue里的computed,核心作用是基于已有响应式数据,生成新的响应式结果,简单说就是“依赖变了,我自动更新”,但选项式API和组合式API的写法差异还挺大,得分开看:
选项式API(Options API)写法
在export default里配置computed选项,对象里每个属性是函数,返回计算后的值,比如做个“姓+名→全名”的功能:
<template>
<div>全名:{{ fullName }}</div>
</template>
<script>
export default {
data() {
return {
firstName: '张',
lastName: '三'
}
},
computed: {
fullName() {
return this.firstName + this.lastName
}
}
}
</script>
这里fullName依赖firstName和lastName,只要这俩值变了,模板里的fullName会自动更新。
组合式API(Composition API)写法
Vue3推荐的组合式API,需要先导入computed,再用函数式写法,还是上面的例子,代码变成这样:
<template>
<div>全名:{{ fullName }}</div>
</template>
<script setup>
import { ref, computed } from 'vue'
const firstName = ref('张')
const lastName = ref('三')
// computed接收一个函数,返回计算后的值
const fullName = computed(() => firstName.value + lastName.value)
</script>
注意哦!组合式API里用了ref,所以访问值得加.value;如果是reactive包裹的对象,直接点属性就行(比如user.firstName)。
computed和methods有啥区别?啥时候该用computed?
很多人初学容易搞混这俩,但核心差异是“缓存”,一句话总结:
methods里的函数,每次调用都重新执行;computed的计算结果,只有依赖的响应式数据变了才重新计算,否则复用缓存值。
举个实际例子:做一个“数组过滤”功能,把数组里大于10的数筛出来。
用methods的写法
<template>
<button @click="changeData">改数据</button>
<div>筛选结果:{{ filterData() }}</div>
</template>
<script setup>
import { ref } from 'vue'
const list = ref([1, 12, 8, 15])
function filterData() {
console.log('methods执行了') // 每次渲染都会打印
return list.value.filter(num => num > 10)
}
function changeData() {
list.value.push(20)
}
</script>
点“改数据”时,list变化导致模板重新渲染,filterData()会每次渲染都执行(哪怕list没变化,只要模板重新渲染,函数就会跑)。
用computed的写法
换成computed试试:
<template>
<button @click="changeData">改数据</button>
<div>筛选结果:{{ filterData }}</div>
</template>
<script setup>
import { ref, computed } from 'vue'
const list = ref([1, 12, 8, 15])
const filterData = computed(() => {
console.log('computed执行了') // 只有list变化时才打印
return list.value.filter(num => num > 10)
})
function changeData() {
list.value.push(20)
}
</script>
只有list真正变化时,filterData才会重新计算;如果其他不相关的响应式数据变化导致模板渲染,filterData会直接复用缓存结果,不会重复执行过滤逻辑。
啥时候选computed?
- 当计算逻辑依赖响应式数据,而且会被多次调用/渲染时,用computed(比如模板里多次显示这个结果,或者在多个地方复用);
- 如果是纯事件触发的逻辑(比如点击按钮执行一次),或者计算逻辑不依赖响应式数据,用methods更合适。
computed的缓存机制咋工作的?为啥有时依赖变了却没更新?
理解computed的“懒计算+缓存”逻辑,能避免很多 Bug。 Vue的响应式系统会自动跟踪computed的依赖项(也就是computed函数里用到的响应式数据),只有这些依赖项变化时,computed才会标记为“脏值”,下次访问时重新计算;如果依赖项没变化,直接返回缓存的结果。
缓存机制的好处
比如做一个“购物车商品总价”的计算,商品列表可能有几十上百条,每次渲染都重新遍历计算会很耗性能,用computed的话,只有商品的“价格”或“数量”变化时,才会重新计算总价,其他时候直接拿缓存,性能友好很多。
为啥依赖变了却不更新?常见坑点
最容易踩的坑是:computed函数里没正确访问响应式数据,导致Vue没法跟踪依赖,举个反面例子:
<script setup>
import { ref, computed } from 'vue'
// 错误示范:用了普通变量,不是响应式的
let normalVar = '我不是响应式的'
const reactiveVar = ref('我是响应式的')
// computed里只用到普通变量,Vue无法跟踪依赖
const wrongComputed = computed(() => normalVar + reactiveVar.value)
function changeNormal() {
normalVar = '变量变了' // 普通变量变化,computed不会更新
}
function changeReactive() {
reactiveVar.value = '响应式变量变了' // 只有这个变化,computed才会更新
}
</script>
这里wrongComputed的依赖应该是normalVar和reactiveVar,但normalVar不是响应式数据,Vue没法检测它的变化,所以即便normalVar改了,wrongComputed也不会更新。
怎么避免?
- 所有在computed里用的可变数据,必须是
ref或reactive包裹的响应式数据; - 如果依赖的是对象里的属性,确保用
reactive包裹对象,或者用ref处理基本类型; - 检查computed函数里的逻辑,是不是真的“访问”到了依赖项(比如别把依赖写在条件判断里,导致没执行到)。
复杂业务逻辑里,computed咋处理多依赖或嵌套情况?
实际项目里,计算逻辑经常要结合多个响应式数据,甚至嵌套其他computed,这时候computed的“组合性”就很重要了——它能像积木一样,把多个小计算逻辑拼起来,让代码更分层。
多依赖场景:购物车总价计算
比如购物车有多个商品,每个商品有price(价格)和count(数量),要计算所有商品的总价:
<script setup>
import { reactive, computed } from 'vue'
// 模拟购物车数据:数组里每个对象是商品
const cart = reactive([
{ id: 1, name: '手机', price: 5000, count: 1 },
{ id: 2, name: '耳机', price: 1000, count: 2 }
])
// 计算单个商品的小计(price * count)
const itemTotals = computed(() => {
return cart.map(good => good.price * good.count)
})
// 计算所有商品的总价(小计之和)
const totalPrice = computed(() => {
return itemTotals.value.reduce((sum, cur) => sum + cur, 0)
})
</script>
<template>
<div v-for="(good, idx) in cart" :key="good.id">
{{ good.name }}:{{ good.price }} × {{ good.count }} = {{ itemTotals[idx] }}
</div>
<div>总价:{{ totalPrice }}</div>
</template>
这里totalPrice依赖itemTotals,而itemTotals依赖cart里的每个商品,当任意商品的price或count变化时,itemTotals先更新,接着totalPrice也会自动更新——逻辑分层清晰,维护起来很方便。
嵌套computed的好处
- 复用性:比如
itemTotals可以在其他地方复用(比如计算优惠前的总价); - 可读性:把大计算拆成小步骤,像“先算每个商品小计→再算总和”,代码逻辑更直观;
- 性能:每个computed只在自己的依赖变化时更新,不会重复计算无关逻辑。
听说computed能设置setter?这在啥场景下有用?
默认情况下,computed是只读的(只有getter),但Vue也支持给computed配置setter,实现“双向计算”,简单说:当你给computed赋值时,会触发setter里的逻辑,能反过来修改依赖的响应式数据。
场景:“全名”拆分到“姓”和“名”
比如用户编辑个人信息时,输入框绑定“全名”,但实际要把值拆分成firstName和lastName存起来,用setter就能实现:
<template>
<input v-model="fullName" placeholder="输入全名(如:张 三)" />
<div>姓:{{ firstName }}</div>
<div>名:{{ lastName }}</div>
</template>
<script setup>
import { ref, computed } from 'vue'
const firstName = ref('')
const lastName = ref('')
const fullName = computed({
// getter:拼接姓和名
get() {
return firstName.value + ' ' + lastName.value
},
// setter:当给fullName赋值时,拆分到firstName和lastName
set(newValue) {
const [f, l] = newValue.split(' ') // 按空格拆分
firstName.value = f || ''
lastName.value = l || ''
}
})
</script>
输入框输入“李 四”,firstName会变成“李”,lastName变成“四”;反过来,修改firstName或lastName,输入框里的fullName也会自动更新——这就是双向的计算逻辑!
啥时候用setter?
- 表单场景:需要把一个输入值拆分成多个字段(出生日期”拆成“年/月/日”);
- 配置项联动:整体开关”控制多个子开关,修改整体开关时,子开关状态同步变化(反过来,所有子开关关闭时,整体开关自动关闭);
- 数据格式化:比如用户输入带单位的数值(如“100px”),setter负责拆分数值和单位,分别存到响应式数据里。
实际开发中,computed有哪些高频使用场景?
光懂语法还不够,得知道在真实项目里咋用,下面这几个场景,几乎每个Vue项目都会碰到,用computed能大幅简化代码~
场景1:表单实时验证
注册页里,密码需要满足“长度≥6、包含大写字母、包含数字”,用computed实时判断密码是否符合规则,比在methods里频繁调用函数更高效:
<template>
<input v-model="password" type="password" placeholder="请输入密码" />
<div v-if="!isPasswordValid" style="color: red;">
密码需≥6位,且包含大写字母和数字
</div>
</template>
<script setup>
import { ref, computed } from 'vue'
const password = ref('')
const isPasswordValid = computed(() => {
const reg = /^(?=.*[A-Z])(?=.*\d).{6,}$/
return reg.test(password.value)
})
</script>
只要password变化,isPasswordValid会自动更新,模板里的错误提示也会实时切换。
场景2:列表筛选/搜索
商品列表页,根据用户输入的关键词过滤商品,用computed把“原列表+关键词”转成“过滤后列表”,模板直接渲染结果:
<template>
<input v-model="searchKey" placeholder="搜索商品名称" />
<ul>
<li v-for="good in filteredGoods" :key="good.id">
{{ good.name }} - ¥{{ good.price }}
</li>
</ul>
</template>
<script setup>
import { reactive, computed } from 'vue'
// 模拟商品数据
const goods = reactive([
{ id: 1, name: '无线鼠标', price: 99 },
{ id: 2, name: '机械键盘', price: 299 },
{ id: 3, name: '蓝牙音箱', price: 199 }
])
const searchKey = reactive('')
// 过滤逻辑写在computed里
const filteredGoods = computed(() => {
return goods.filter(good =>
good.name.includes(searchKey)
)
})
</script>
用户输入时,searchKey变化→filteredGoods自动更新→列表自动重新渲染,逻辑和模板解耦得很干净。
场景3:数据格式化
后端返回的时间戳、图片路径等,需要前端格式化后展示,用computed把“原始数据”转成“展示用数据”,模板里不用写重复逻辑:
<template>
<div>发布时间:{{ formatTime }}</div>
<img :src="formatAvatar" alt="用户头像" />
</template>
<script setup>
import { ref, computed } from 'vue'
const rawTime = ref(1699999999000) // 后端给的时间戳
const rawAvatar = ref('user_avatar.png') // 后端给的头像路径
const baseImgUrl = 'https://xxx.cdn.com/' // 图片CDN基础地址
// 时间戳转YYYY-MM-DD
const formatTime = computed(() => {
const date = new Date(rawTime.value)
return date.getFullYear() + '-' + (date.getMonth() + 1) + '-' + date.getDate()
})
// 拼接头像完整URL
const formatAvatar = computed(() => {
return baseImgUrl + rawAvatar.value
})
</script>
以后如果要改时间格式或图片CDN地址,只需要改computed里的逻辑,不用动模板,维护性拉满~
场景4:购物车多维度计算
购物车页面需要计算“商品总价”“优惠后价格”“可选商品合计”等多个关联数据,用多个computed拆分逻辑,代码清晰又高效:
<template>
<div>商品总价:{{ totalPrice }}</div>
<div>优惠后价格:{{ discountPrice }}</div>
<div>可选商品合计:{{ selectedTotal }}</div>
</template>
<script setup>
import { reactive, computed } from 'vue'
const cart = reactive([
{ id: 1, name: '键盘', price: 299, count: 1, selected: true },
{ id: 2, name: '鼠标', price: 99, count: 2, selected: false },
{ id: 3, name: '耳机', price: 199, count: 1, selected: true }
])
const discountRate = 0.9 // 9折优惠
// 计算所有商品的总价(不管是否选中)
const totalPrice = computed(() => {
return cart.reduce((sum, good) => sum + good.price * good.count, 0)
})
// 计算优惠后价格(总价×折扣)
const discountPrice = computed(() => {
return totalPrice.value * discountRate
})
// 计算选中商品的合计
const selectedTotal = computed(() => {
return cart.filter(good => good.selected)
.reduce((sum, good) => sum + good.price * good.count, 0)
})
</script>
每个computed只负责一个维度的计算,依赖变化时精准更新,比把所有逻辑塞在一个函数里好维护太多~
computed的核心价值
从基础用法到复杂场景,computed的本质是“用声明式的方式,处理响应式数据的派生逻辑”,它帮我们解决了这些问题:
- 减少重复计算,通过缓存提升性能;
- 把复杂逻辑从模板和methods里抽离,让代码更易读、易维护;
- 自动跟踪依赖,实现“数据变了,结果自动更新”的响应式体验。
下次写代码时,碰到“基于已有
版权声明
本文仅代表作者观点,不代表Code前端网立场。
本文系作者Code前端网发表,如需转载,请注明页面地址。
code前端网


