news 2026/5/1 12:13:24

Vue.js 计算属性

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Vue.js 计算属性

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('') }) </script>

2. 为什么使用计算属性?

  • 缓存性:只有依赖变化时才重新计算,性能优于方法。
  • 声明式:模板中直接调用,逻辑清晰。
  • 可维护性:复杂逻辑抽离到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)

当需要双向绑定派生数据时,可以定义getset

场景:拆分全名为姓和名

<template> <div> <p>全名:{{ fullName }}</p> <input v-model="firstName" placeholder="姓" /> <input v-model="lastName" 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-model="fullName"会触发set方法。
  • firstNamelastName变化时,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以提升性能。


五、计算属性的依赖追踪

计算属性会自动追踪其内部使用的所有响应式数据refreactiveprops等)。

<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-for="item in cartItems" :key="item.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) }) </script>

2. 列表过滤与排序

<template> <div> <input v-model="searchQuery" placeholder="搜索..." /> <ul> <li v-for="user in filteredUsers" :key="user.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 }) </script>

3. 表单验证

<template> <div> <input v-model="username" placeholder="用户名" /> <p v-if="usernameError" style="color: red">{{ usernameError }}</p> <input v-model="password" type="password" placeholder="密码" /> <p v-if="passwordError" style="color: 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>

七、最佳实践

  1. 保持简洁:计算属性逻辑应简单清晰,避免复杂副作用。
  2. 逻辑外移:复杂逻辑可抽离为独立的函数,在computed中调用。
  3. 避免副作用:不要在computed中修改其他响应式数据(如ref.value = ...),这会导致无限循环。
  4. 优先使用computed:对于派生数据,优先使用computed而非methods
  5. 命名规范:计算属性通常以名词命名,如fullNametotalPricefilteredUsers

八、常见陷阱

陷阱 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 响应式系统的核心特性之一,合理使用可以大幅提升代码的可读性和性能。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/5/1 12:11:28

手把手教你用PaddleOCR的SVTR模型:从环境搭建到中文场景文字识别实战

手把手教你用PaddleOCR的SVTR模型&#xff1a;从环境搭建到中文场景文字识别实战 在数字化浪潮席卷各行各业的今天&#xff0c;光学字符识别&#xff08;OCR&#xff09;技术已成为连接物理世界与数字世界的桥梁。无论是金融行业的票据处理、零售行业的商品标签识别&#xff0c…

作者头像 李华
网站建设 2026/5/1 12:09:24

告别裸机轮询:用沁恒CH582的TMOS构建高效低功耗蓝牙应用实战

告别裸机轮询&#xff1a;用沁恒CH582的TMOS构建高效低功耗蓝牙应用实战 在嵌入式开发领域&#xff0c;资源受限的MCU上实现多任务调度一直是个棘手问题。许多开发者习惯使用简单的while(1)轮询来处理按键扫描、传感器采集、蓝牙通信等并发需求&#xff0c;但这种粗暴的方式往往…

作者头像 李华
网站建设 2026/5/1 12:04:17

告别Keil V4兼容烦恼:手把手教你将GD32F303官方例程迁移到Keil 5.15

告别Keil V4兼容烦恼&#xff1a;手把手教你将GD32F303官方例程迁移到Keil 5.15 当你拿到GD32F303官方提供的例程包&#xff0c;满心欢喜准备开始开发时&#xff0c;却发现工程文件无法直接打开——这种场景对于使用Keil MDK 5的开发者来说再熟悉不过。本文将带你一步步解决这个…

作者头像 李华
网站建设 2026/5/1 12:03:09

短视频陪跑源头厂家

在当今的数字化时代&#xff0c;短视频已经成为品牌传播和营销的重要工具。然而&#xff0c;对于许多企业来说&#xff0c;如何制作高质量的短视频、如何进行有效的运营&#xff0c;仍然是一个挑战。本文将从几个方面探讨如何选择合适的短视频陪跑源头厂家&#xff0c;并提供具…

作者头像 李华