嘿,各位前端摸鱼大师们,今天咱们来聊聊Vue3中那个能让代码自己“偷懒”的神奇功能——computed()。不知道你们有没有遇到过这种情况:页面上有个数据,需要根据其他好几个数据计算出来,然后你就在那儿手动监听这个、监听那个,写了一大堆代码,最后自己都绕晕了?
来,举个栗子🌰:你在做一个购物车,商品列表变了,总价要重新计算;商品数量变了,总价也要重新计算;甚至优惠券变了,总价还得重新计算... 这时候你是不是很想仰天长啸:“能不能让我消停会儿?!”
别急,computed()就是来拯救你的超级英雄!
一、computed()是什么鬼?
简单来说,computed()是Vue3组合式API中的一个响应式API,它能创建一个响应式的计算属性。这个计算属性的值会根据它依赖的其他响应式数据自动计算出来,而且只有在依赖发生变化时才会重新计算。
说白了,它就是你的私人小会计,你只需要告诉它:“小computed啊,帮我盯着这几个数,它们一变你就帮我算一下这个结果。”然后你就可以拍拍屁股走人,剩下的交给它了。
computed()的三大绝活:
- 自动追踪依赖:它像个侦探一样,能自动发现计算过程中用了哪些响应式数据,然后盯着它们。
- 缓存结果:这是最牛逼的地方!如果依赖的数据没变,它就直接返回上次计算的结果,不会重新计算。想想你的excel表格,某个单元格公式依赖的其他单元格没变,它就不会重新计算,一样的道理。
- 响应式更新:只要依赖的数据一变,它立刻重新计算,并且通知所有用到这个计算值的地方更新。
二、computed()怎么用?超简单!
使用computed()有两种方式,跟点菜一样,有简版和精装版。
方式1:只读模式(最常用)
这就是个老实本分的计算属性,只负责计算,不能直接修改。
import { ref, computed } from 'vue'
// 假设我们在setup函数里或者<script setup>中
const price = ref(10) // 单价
const count = ref(2) // 数量
// 创建一个计算属性:总价 = 单价 × 数量
const total = computed(() => {
console.log('重新计算总价啦!') // 依赖变化时才会执行
return price.value * count.value
})
// 试试看
console.log(total.value) // 输出:20,同时控制台会打印"重新计算总价啦!"
// 修改数量
count.value = 3
console.log(total.value) // 输出:30,控制台再次打印
// 再次访问,但依赖没变
console.log(total.value) // 输出:30,但控制台不会打印,因为用了缓存!
方式2:可读写模式(进阶玩法)
这种模式下,你既可以获取计算值,也可以设置它。设置时会触发一个回调函数,让你决定怎么处理。
import { ref, computed } from 'vue'
const firstName = ref('张')
const lastName = ref('三')
// 可读写的计算属性
const fullName = computed({
// 获取时计算全名
get() {
return `${firstName.value}${lastName.value}`
},
// 设置时拆分名字
set(newValue) {
console.log('有人想改全名:', newValue)
// 假设新值是"李四"
const names = newValue.split('')
firstName.value = names[0] // "李"
lastName.value = names[1] // "四"
}
})
// 获取
console.log(fullName.value) // "张三"
// 设置
fullName.value = "李四"
console.log(firstName.value) // "李"
console.log(lastName.value) // "四"
是不是感觉已经开始香起来了?但这只是开胃小菜,下面咱们来点真家伙!
三、实战示例1:智能购物车
来,咱们用computed()实现一个会自己算账的智能购物车。
<template>
<div class="cart">
<h2>我的购物车 🛒</h2>
<div v-for="item in items" :key="item.id" class="cart-item">
<span>{{ item.name }} - ¥{{ item.price }}</span>
<div>
<button @click="decrease(item.id)">-</button>
<span class="count">{{ item.count }}</span>
<button @click="increase(item.id)">+</button>
<button @click="remove(item.id)" class="remove">删除</button>
</div>
</div>
<!-- 这里开始都是计算属性! -->
<div class="summary">
<p>商品总数:{{ totalCount }} 件</p>
<p>商品总价:¥{{ totalPrice }}</p>
<p v-if="hasDiscount">优惠折扣:-¥{{ discountAmount }}</p>
<p class="final-price">实付金额:<strong>¥{{ finalPrice }}</strong></p>
<p class="saving" v-if="hasDiscount">您节省了:¥{{ discountAmount }} 🎉</p>
</div>
</div>
</template>
<script setup>
import { ref, computed } from 'vue'
// 原始数据 - 购物车商品
const items = ref([
{ id: 1, name: 'Vue实战指南', price: 68, count: 1 },
{ id: 2, name: 'JavaScript高级程序设计', price: 99, count: 2 },
{ id: 3, name: 'CSS揭秘', price: 55, count: 1 }
])
// 优惠券相关
const hasCoupon = ref(true)
const discountRate = 0.8 // 8折
// 操作方法
const increase = (id) => {
const item = items.value.find(item => item.id === id)
if (item) item.count++
}
const decrease = (id) => {
const item = items.value.find(item => item.id === id)
if (item && item.count > 1) {
item.count--
}
}
const remove = (id) => {
items.value = items.value.filter(item => item.id !== id)
}
// 🎯 重点来了!计算属性大军登场!
// 1. 计算商品总数量
const totalCount = computed(() => {
return items.value.reduce((sum, item) => sum + item.count, 0)
})
// 2. 计算商品总价
const totalPrice = computed(() => {
return items.value.reduce((sum, item) => sum + (item.price * item.count), 0)
})
// 3. 判断是否有折扣
const hasDiscount = computed(() => {
return hasCoupon.value && totalPrice.value >= 100 // 满100才能用优惠券
})
// 4. 计算折扣金额
const discountAmount = computed(() => {
if (!hasDiscount.value) return 0
return totalPrice.value * (1 - discountRate)
})
// 5. 计算最终价格
const finalPrice = computed(() => {
return totalPrice.value - discountAmount.value
})
</script>
<style scoped>
.cart {
max-width: 500px;
margin: 0 auto;
padding: 20px;
}
.cart-item {
display: flex;
justify-content: space-between;
align-items: center;
padding: 10px;
border-bottom: 1px solid #eee;
}
.count {
margin: 0 10px;
font-weight: bold;
}
.remove {
margin-left: 10px;
color: #ff4757;
}
.summary {
margin-top: 20px;
padding: 15px;
background: #f8f9fa;
border-radius: 8px;
}
.final-price {
font-size: 1.2em;
color: #2ed573;
}
.saving {
color: #ffa502;
}
button {
padding: 5px 10px;
border: 1px solid #ddd;
background: white;
cursor: pointer;
border-radius: 4px;
}
button:hover {
background: #f1f2f6;
}
</style>
这个购物车的智能之处:
- 你只管增删商品,其他的都交给computed属性
- 总数量、总价格自动更新
- 满足优惠条件时,折扣自动计算
- 所有计算都是惰性的,只有相关数据变化时才重新计算
四、实战示例2:智能搜索过滤器
再来个更实用的:列表搜索过滤。用户在输入框打字,列表实时过滤,还显示统计信息。
<template>
<div class="search-demo">
<h2>员工查找器 🔍</h2>
<div class="controls">
<input
v-model="searchKeyword"
placeholder="输入姓名或部门搜索..."
class="search-input"
>
<select v-model="selectedDepartment" class="department-select">
<option value="">所有部门</option>
<option value="技术部">技术部</option>
<option value="市场部">市场部</option>
<option value="人事部">人事部</option>
</select>
</div>
<!-- 统计信息都是计算属性 -->
<div class="stats">
<p>
显示 {{ filteredEmployees.length }} /
总共 {{ employees.length }} 名员工
<span v-if="hasSearch">(搜索到 {{ searchResultCount }} 个结果)</span>
</p>
</div>
<!-- 过滤后的员工列表 -->
<div class="employee-list">
<div
v-for="employee in filteredEmployees"
:key="employee.id"
class="employee-card"
>
<h3>{{ employee.name }}</h3>
<p>部门:{{ employee.department }}</p>
<p>职位:{{ employee.position }}</p>
<p>邮箱:{{ employee.email }}</p>
</div>
</div>
<div v-if="noResults" class="no-results">
😢 没有找到匹配的员工
</div>
</div>
</template>
<script setup>
import { ref, computed } from 'vue'
// 原始数据 - 员工列表
const employees = ref([
{ id: 1, name: '张三', department: '技术部', position: '前端工程师', email: 'zhangsan@company.com' },
{ id: 2, name: '李四', department: '技术部', position: '后端工程师', email: 'lisi@company.com' },
{ id: 3, name: '王五', department: '市场部', position: '市场经理', email: 'wangwu@company.com' },
{ id: 4, name: '赵六', department: '人事部', position: 'HR专员', email: 'zhaoliu@company.com' },
{ id: 5, name: '钱七', department: '技术部', position: '架构师', email: 'qianqi@company.com' }
])
// 搜索条件
const searchKeyword = ref('')
const selectedDepartment = ref('')
// 🎯 计算属性开始表演!
// 1. 过滤后的员工列表
const filteredEmployees = computed(() => {
let result = employees.value
// 按部门过滤
if (selectedDepartment.value) {
result = result.filter(emp =>
emp.department === selectedDepartment.value
)
}
// 按关键词搜索
if (searchKeyword.value.trim()) {
const keyword = searchKeyword.value.toLowerCase()
result = result.filter(emp =>
emp.name.toLowerCase().includes(keyword) ||
emp.department.toLowerCase().includes(keyword) ||
emp.position.toLowerCase().includes(keyword)
)
}
return result
})
// 2. 搜索结果的统计
const searchResultCount = computed(() => {
return filteredEmployees.value.length
})
// 3. 判断是否有搜索条件
const hasSearch = computed(() => {
return searchKeyword.value.trim() || selectedDepartment.value
})
// 4. 判断是否没有结果
const noResults = computed(() => {
return hasSearch.value && filteredEmployees.value.length === 0
})
</script>
<style scoped>
.search-demo {
max-width: 600px;
margin: 0 auto;
padding: 20px;
}
.controls {
display: flex;
gap: 10px;
margin-bottom: 20px;
}
.search-input, .department-select {
padding: 8px 12px;
border: 1px solid #ddd;
border-radius: 4px;
flex: 1;
}
.stats {
margin-bottom: 15px;
color: #666;
}
.employee-list {
display: grid;
gap: 10px;
}
.employee-card {
padding: 15px;
border: 1px solid #e9ecef;
border-radius: 8px;
background: white;
}
.employee-card h3 {
margin: 0 0 10px 0;
color: #2c3e50;
}
.no-results {
text-align: center;
padding: 40px;
color: #7f8c8d;
font-size: 1.1em;
}
</style>
这个搜索过滤器的亮点:
- 输入关键词或选择部门时,列表实时过滤
- 统计信息自动更新
- 没有结果时显示友好提示
- 所有过滤逻辑都封装在computed属性中,模板超级干净
五、computed()的进阶技巧和避坑指南
技巧1:组合多个computed属性
computed属性可以依赖其他computed属性,形成计算链:
const basePrice = computed(() => price.value * count.value)
const tax = computed(() => basePrice.value * 0.1)
const finalPrice = computed(() => basePrice.value + tax.value)
技巧2:在watch中使用computed
import { watch } from 'vue'
// 监听计算属性的变化
watch(finalPrice, (newPrice, oldPrice) => {
console.log(`价格从 ${oldPrice} 变为 ${newPrice}`)
})
避坑指南:
- 不要修改computed值(除非使用get/set模式)
- 避免在computed中有副作用(比如异步请求、修改DOM)
- 计算函数应该是纯函数,同样的输入永远得到同样的输出
六、computed() vs methods vs watch
- computed:适合派生数据,有缓存,响应式依赖
- methods:适合事件处理,无缓存,每次调用都执行
- watch:适合监听变化执行异步操作或复杂逻辑
简单记:要计算用computed,要动作用methods,要监听用watch。
总结
computed()就像是给你的Vue应用请了个不知疲倦的会计,你只需要定义好计算规则,它就自动帮你把所有的账算得明明白白。而且这个会计还特别聪明,同样的账不会算第二遍(缓存),只有数字变了才重新算。
学会了computed(),你会发现以前那些繁琐的手动更新、重复计算都是浮云。代码更简洁、性能更高、维护更容易,关键是——你可以更理直气壮地摸鱼了!
记住,好的程序员不是写代码最多的,而是写代码最聪明的。computed()就是让你变聪明的神器之一!
快去给你的Vue项目装上这个"自动计算大脑"吧,保证你用了就回不去了!
3208

被折叠的 条评论
为什么被折叠?



