Vue.js 计算属性 (computed) 学习笔记计算属性是 Vue 中处理派生数据的核心机制。当数据依赖于其他响应式数据时使用计算属性可以自动追踪依赖、缓存结果并保持模板的简洁性。一、核心概念1. 什么是计算属性计算属性是基于它们的响应式依赖进行缓存的。只有当依赖的响应式数据发生变化时计算属性才会重新求值。template p原始消息{{ message }}/p p反转消息{{ reversedMessage }}/p /template script setup import { ref, computed } from vue const message ref(Hello Vue) // 计算属性自动缓存依赖 message 变化时才重新计算 const reversedMessage computed(() { return message.value.split().reverse().join() }) /script2. 为什么使用计算属性缓存性只有依赖变化时才重新计算性能优于方法。声明式模板中直接调用逻辑清晰。可维护性复杂逻辑抽离到computed模板保持简洁。二、只读计算属性最基本的用法仅用于读取派生数据。template div p价格{{ price }}/p p折扣价 (8 折){{ discountPrice }}/p p含税价{{ taxPrice }}/p /div /template script setup import { ref, computed } from vue const price ref(100) const taxRate 0.1 // 计算属性 const discountPrice computed(() price.value * 0.8) const taxPrice computed(() price.value * (1 taxRate)) /script注意在模板中调用计算属性不需要加括号因为它是一个属性而非方法。三、可写计算属性 (Getter Setter)当需要双向绑定派生数据时可以定义get和set。场景拆分全名为姓和名template div p全名{{ fullName }}/p input v-modelfirstName placeholder姓 / input v-modellastName placeholder名 / /div /template script setup import { ref, computed } from vue const firstName ref(张) const lastName ref(三) // 可写计算属性 const fullName computed({ // 读取时拼接姓和名 get() { return firstName.value lastName.value }, // 写入时拆分全名 set(newValue) { // 假设全名格式为 姓 名 const [first, last] newValue.split( ) firstName.value first || lastName.value last || } }) /script原理v-modelfullName会触发set方法。当firstName或lastName变化时get方法自动重新计算。四、computedvsmethods特性computedmethods缓存✅ 有缓存依赖不变不重算❌ 无缓存每次调用都执行调用方式{{ fullName }}(无括号){{ getFullName() }}(需括号)依赖追踪自动追踪响应式依赖无自动追踪适用场景需要缓存的派生数据每次都需要重新计算、或涉及副作用对比示例template div !-- 计算属性缓存依赖 message 变化才重算 -- p计算属性{{ reversedMessage }}/p !-- 方法每次渲染都执行 -- p方法{{ getReversedMessage() }}/p /div /template script setup import { ref, computed } from vue const message ref(Hello) // 计算属性 const reversedMessage computed(() { console.log(计算属性执行) return message.value.split().reverse().join() }) // 方法 function getReversedMessage() { console.log(方法执行) return message.value.split().reverse().join() } /script输出观察初始渲染计算属性执行 1 次方法执行 1 次。再次渲染无数据变化计算属性不执行方法执行。message变化计算属性执行 1 次方法执行 1 次。结论对于复杂的计算优先使用computed以提升性能。五、计算属性的依赖追踪计算属性会自动追踪其内部使用的所有响应式数据ref、reactive、props等。script setup import { ref, computed } from vue const firstName ref(张) const lastName ref(三) const age ref(25) // 依赖 firstName 和 lastName const fullName computed(() firstName.value lastName.value) // 依赖 age const isAdult computed(() age.value 18) // 依赖多个计算属性 const description computed(() { return ${fullName.value} 今年 ${age.value} 岁${isAdult.value ? 成年 : 未成年} }) /script注意非响应式数据如普通变量不会被追踪。六、实战示例1. 购物车总价计算template div ul li v-foritem in cartItems :keyitem.id {{ item.name }} x {{ item.quantity }} ¥{{ (item.price * item.quantity).toFixed(2) }} /li /ul p商品数量{{ totalQuantity }}/p p总价¥{{ totalPrice }}/p /div /template script setup import { ref, computed } from vue const cartItems ref([ { id: 1, name: iPhone, price: 6999, quantity: 1 }, { id: 2, name: AirPods, price: 1899, quantity: 2 } ]) // 计算总数量 const totalQuantity computed(() { return cartItems.value.reduce((sum, item) sum item.quantity, 0) }) // 计算总价 const totalPrice computed(() { return cartItems.value.reduce((sum, item) sum item.price * item.quantity, 0) }) /script2. 列表过滤与排序template div input v-modelsearchQuery placeholder搜索... / ul li v-foruser in filteredUsers :keyuser.id {{ user.name }} ({{ user.age }}岁) /li /ul /div /template script setup import { ref, computed } from vue const users ref([ { id: 1, name: 张三, age: 25 }, { id: 2, name: 李四, age: 30 }, { id: 3, name: 王五, age: 28 } ]) const searchQuery ref() // 过滤 排序 const filteredUsers computed(() { let result users.value // 过滤 if (searchQuery.value) { result result.filter(user user.name.includes(searchQuery.value) ) } // 排序按年龄 result result.sort((a, b) a.age - b.age) return result }) /script3. 表单验证template div input v-modelusername placeholder用户名 / p v-ifusernameError stylecolor: red{{ usernameError }}/p input v-modelpassword typepassword placeholder密码 / p v-ifpasswordError stylecolor: red{{ passwordError }}/p button :disabled!isFormValid提交/button /div /template script setup import { ref, computed } from vue const username ref() const password ref() // 用户名验证 const usernameError computed(() { if (!username.value) return 用户名不能为空 if (username.value.length 3) return 用户名至少 3 个字符 return }) // 密码验证 const passwordError computed(() { if (!password.value) return 密码不能为空 if (password.value.length 6) return 密码至少 6 个字符 return }) // 表单是否有效 const isFormValid computed(() { return !usernameError.value !passwordError.value }) /script七、最佳实践保持简洁计算属性逻辑应简单清晰避免复杂副作用。逻辑外移复杂逻辑可抽离为独立的函数在computed中调用。避免副作用不要在computed中修改其他响应式数据如ref.value ...这会导致无限循环。优先使用computed对于派生数据优先使用computed而非methods。命名规范计算属性通常以名词命名如fullName、totalPrice、filteredUsers。八、常见陷阱陷阱 1在computed中修改响应式数据!-- ❌ 错误导致无限循环 -- const doubleCount computed(() { count.value * 2 // 修改依赖触发重新计算无限循环 return count.value * 2 })陷阱 2使用非响应式数据script setup import { ref, computed } from vue const count ref(0) const multiplier 2 // 普通变量非响应式 // ❌ 修改 multiplier 不会触发重新计算 const doubled computed(() count.value * multiplier) /script修正将multiplier改为ref。const multiplier ref(2) const doubled computed(() count.value * multiplier.value)陷阱 3忘记.value在script setup中访问ref时必须使用.value。script setup import { ref, computed } from vue const name ref(Vue) // ❌ 错误name 是 ref 对象不是字符串 const upper computed(() name.toUpperCase()) // ✅ 正确 const upper computed(() name.value.toUpperCase()) /script计算属性是 Vue 响应式系统的核心特性之一合理使用可以大幅提升代码的可读性和性能。