Vue3里computed咋用?常见疑问+实战技巧一次讲透
很多刚学Vue3的同学,对computed(计算属性)既好奇又有点懵:它和methods有啥区别?缓存啥时候生效?能不能做异步操作?今天用问答形式把这些痛点一次讲明白,还带实战例子,看完就会用~
Vue3的computed是干啥的?
简单说,computed是基于响应式数据生成新值的“自动计算器”,比如你有一个响应式变量count,想生成它的平方值,用computed就不用每次手动计算。
举个栗子:
import { ref, computed } from 'vue'
const count = ref(2)
const squared = computed(() => count.value * count.value)
console.log(squared.value) // 输出4
count.value = 3
console.log(squared.value) // 自动变成9
这里count是响应式数据(用ref包裹),squared会“盯住”count的变化 —— 只要count变了,squared就自动重新计算,而且计算结果会被缓存,如果count没变化,多次访问squared.value不会重复计算,直接拿缓存结果,省性能~
computed和methods有啥区别?选哪个?
最核心的区别是“缓存机制”和“调用时机”。
- methods:每次调用函数都会重新执行逻辑,比如写个
getSquared()方法,不管count变没变,每次调用getSquared()都会重新算一遍。 - computed:只有依赖的响应式数据变化时,才会重新计算;如果依赖没变化,直接复用之前的结果(缓存)。
举个场景对比:
做一个“购物车商品总价”功能,总价依赖每个商品的price和quantity,如果用methods,每次渲染页面或用户操作时,哪怕商品数据没变化,都会重新遍历所有商品计算总价;用computed的话,只有当某个商品的price或quantity变了,才会重新计算总价,性能更好。
总结选择逻辑:
- 复杂逻辑需要“自动追踪依赖、缓存结果”时,用
computed(比如数据过滤、多变量计算)。 - 事件处理、一次性逻辑(比如点击按钮执行动作),用
methods。
computed的缓存啥时候生效?为啥有时不生效?
缓存生效的关键是“依赖的响应式数据是否变化”。
缓存生效的情况:
只有当computed依赖的响应式数据(比如ref、reactive包裹的变量)发生改变时,computed才会重新计算,比如前面的count是ref包裹的,它变了,squared才会重新算。
缓存不生效的常见坑:
- 依赖非响应式数据:如果
computed里用了普通变量(没被ref/reactive包裹),Vue没法追踪变化,自然不会重新计算。
反例:let normalNum = 2 // 普通变量,非响应式 const wrongComputed = computed(() => normalNum * normalNum) normalNum = 3 // 这里修改普通变量,computed不会更新! console.log(wrongComputed.value) // 还是4
- 手动修改computed本身(没写setter):
computed默认是“只读”的(只有getter),如果直接赋值xxx.value = 新值,会报错,这种情况下“修改computed”本身不算“依赖变化”,自然没效果。
能给computed加setter吗?啥场景用?
可以!默认computed只有getter(负责计算值),但也能配置setter,让computed支持双向修改。
啥场景需要setter?
比如做一个“全名”功能:用户可以输入“姓+名”(如“张三”),也能分别修改“姓”和“名”,这时候用computed的setter拆分赋值很方便。
代码示例:
import { ref, computed } from 'vue'
const firstName = ref('张')
const lastName = ref('三')
const fullName = computed({
// getter:计算全名
get() {
return firstName.value + lastName.value
},
// setter:拆分全名到姓和名
set(newValue) {
// 假设用户输入“李四”,拆分成firstName='李', lastName='四'
const [first, last] = newValue.split('')
firstName.value = first
lastName.value = last
}
})
// 场景1:通过getter获取全名
console.log(fullName.value) // 输出“张三”
// 场景2:通过setter修改全名
fullName.value = '李四'
console.log(firstName.value) // 输出“李”
console.log(lastName.value) // 输出“四”
这种“双向联动”的场景,用computed的setter比写多个方法简洁多了~
依赖多个响应式数据时,computed咋工作?
Vue3的computed会自动追踪所有依赖的响应式数据,只要其中任意一个变化,就会重新计算。
实战:购物车总价计算
假设购物车数据是这样的响应式数组:
import { reactive, computed } from 'vue'
const cart = reactive([
{ id: 1, price: 10, quantity: 2 },
{ id: 2, price: 20, quantity: 3 }
])
const totalPrice = computed(() => {
return cart.reduce((sum, item) => {
return sum + item.price * item.quantity
}, 0)
})
console.log(totalPrice.value) // 10*2 + 20*3 = 80
// 修改其中一个商品的quantity
cart[0].quantity = 3
console.log(totalPrice.value) // 10*3 + 20*3 = 90
// 新增商品
cart.push({ id: 3, price: 15, quantity: 2 })
console.log(totalPrice.value) // 90 + 15*2 = 120
可以看到,不管是修改已有商品的属性,还是新增商品(改变cart数组本身),computed都会自动感知变化,重新计算总价,这就是Vue的“响应式追踪”在起作用~
实战:用computed做列表搜索过滤
很多项目里需要“输入关键词,实时过滤列表”,用computed特别顺手。
步骤拆解:
- 定义响应式的搜索关键词(用户输入的内容)。
- 定义原始列表数据(比如从接口获取的数组)。
- 用
computed根据关键词过滤列表,生成“过滤后的数据”。
代码示例:
<template>
<input v-model="searchQuery" placeholder="搜索商品" />
<ul>
<li v-for="item in filteredList" :key="item.id">{{ item.name }}</li>
</ul>
</template>
<script setup>
import { ref, computed } from 'vue'
// 原始商品列表(假设从接口拿到的)
const products = ref([
{ id: 1, name: 'Vue3实战' },
{ id: 2, name: 'JavaScript进阶' },
{ id: 3, name: '前端工程化' }
])
// 搜索关键词(双向绑定到输入框)
const searchQuery = ref('')
// 计算属性:过滤后的列表
const filteredList = computed(() => {
// 转成小写,做不区分大小写的搜索
const query = searchQuery.value.toLowerCase()
return products.value.filter(product => {
return product.name.toLowerCase().includes(query)
})
})
</script>
用户输入时,searchQuery变化 → computed自动重新过滤列表 → 页面实时更新,整个过程不用手动写“监听输入、调用过滤函数”的逻辑,computed帮我们自动做了~
computed里能写异步操作吗?为啥?
不能直接写异步!因为computed的设计是“同步返回计算结果”,而异步操作(比如Promise、async/await)没法立刻返回值。
那需要异步计算属性咋办?
得结合ref和watch手动实现“异步版计算属性”,举个例子,要根据用户ID异步获取用户信息:
import { ref, watch } from 'vue'
const userId = ref(1)
const userInfo = ref(null)
// 用watch监听userId变化,触发异步请求
watch(userId, async (newId) => {
const res = await fetch(`/api/user/${newId}`)
userInfo.value = await res.json()
})
// 初始加载
watch(userId, { immediate: true })
这里userInfo是响应式数据,依赖userId的变化触发异步请求,虽然不是直接用computed,但实现了“基于响应式数据的异步计算”效果~
computed的核心价值
- 自动响应:依赖的响应式数据变了,自动重新计算。
- 性能优化:依赖不变时复用缓存结果,减少不必要的计算。
- 逻辑复用:把复杂的“数据推导逻辑”封装成计算属性,让模板和脚本更简洁。
现在再回头看开头的问题,是不是清晰多了?下次写Vue3代码时,遇到“需要根据已有数据生成新值、且希望自动更新”的场景,别忘用computed呀~
(如果还有其他疑问,比如computed和watch的区别、更复杂的依赖场景,评论区留言,下次展开讲~)
版权声明
本文仅代表作者观点,不代表Code前端网立场。
本文系作者Code前端网发表,如需转载,请注明页面地址。
code前端网



