当前位置: 首页 > news >正文

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

http://www.jsqmd.com/news/731564/

相关文章:

  • 高效创建4K 240Hz虚拟显示器:ParsecVDisplay完整指南
  • 不止于卡车:J1939协议在非道路机械(农机、工程车)上的应用与调试实战
  • 如何将B站缓存视频永久保存?3分钟掌握m4s转MP4终极免费方案
  • 分布式多车自主泊车系统设计与Autoware实践
  • TVA在机器人核心零部件制造与检测中的体验分享(1)
  • 基于AI与静态生成的智能RSS聚合器FeedMe部署与定制指南
  • 构建内容审核系统时集成 Taotoken 多模型 API 的策略
  • 键盘连击终结者:Keyboard Chatter Blocker完全配置指南
  • 什么是安可?安可即时通讯软件选型标准 - 小天互连即时通讯
  • 使用 curl 在 Ubuntu 终端快速验证 Taotoken API Key 与网络连通性
  • 别再只用X-Frame-Options了!深入对比Content-Security-Policy的frame-ancestors,为你的Web应用选择最佳防嵌套策略
  • Sunshine游戏串流服务器终极实战指南:零基础打造你的专属云游戏平台
  • 为你的开源项目集成大模型能力利用 Taotoken 实现快速原型验证
  • 3ds Max 2024导入文件格式大全:从CAD到动画,新手必知的10种核心格式与实战操作
  • 有人AI算力主机 | 多源数据,AI分析,边缘智理
  • [具身智能-516]:致五一节:AI时代,劳动的第一需要与中文世界的“锦上添花”
  • 从dev到prod只需1次git push:基于renv+GitHub Packages+RSPM的Tidyverse依赖全生命周期管控体系
  • 保姆级教程:一招判断你的Pixel是Verizon版还是无锁版(附解锁OEM避坑指南)
  • 告别Keil V4兼容烦恼:手把手教你将GD32F303官方例程迁移到Keil 5.15
  • 3步轻松升级:用OpenCore Legacy Patcher让旧Mac焕发新生
  • 告别裸机轮询:用沁恒CH582的TMOS构建高效低功耗蓝牙应用实战
  • 长期使用taotoken聚合服务对项目运维复杂度的实际影响
  • Maccy:重塑你的剪贴板思维,让每一次复制都成为智慧资产
  • 别再乱删C盘了!一文搞懂Windows AppData里Local、Roaming、LocalLow的区别与清理指南
  • 遥感小白必看:用QGIS内置浏览器三步搞定Landsat 8/9数据下载与预览
  • 手把手教你用GoT框架优化GPT-3.5/4的排序与关键词统计任务,成本直降30%+
  • ThinkPHP6路由规则详解:除了基础用法,这些‘隐藏’技巧让URL更优雅
  • 探索qmcdump:揭秘QQ音乐加密格式的解码实战
  • AI写专著实战指南:借助AI工具,一周完成20万字专著撰写!
  • 体验 Taotoken 官方价折扣与活动价对项目长期运行的成本影响