一、computed 的核心特性

  1. 依赖缓存
    基于响应式依赖自动缓存计算结果,只有依赖项变化时才会重新计算
  2. 声明式编程
    将复杂计算逻辑封装为可读性高的属性,避免模板中出现复杂表达式
  3. 自动追踪依赖
    Vue 自动追踪计算属性依赖的响应式数据,无需手动管理更新
  4. 懒计算机制
    依赖变化后不会立即计算,直到下次被访问时才重新求值

二、基础用法(组合式 API)

1. 简单计算

<script setup>
import { ref, computed } from 'vue'

const price = ref(100)
const quantity = ref(2)

// 计算总价
const total = computed(() => price.value * quantity.value)
</script>

<template>
  <p>单价:{{ price }}</p>
  <p>数量:{{ quantity }}</p>
  <p>总价:{{ total }}</p>
</template>

2. 多依赖计算

<script setup>
import { ref, computed } from 'vue'

const firstName = ref('张')
const lastName = ref('三')

// 组合全名
const fullName = computed(() => `${firstName.value} ${lastName.value}`)
</script>

三、高级用法

1. 可写计算属性(setter)

<script setup>
import { ref, computed } from 'vue'

const firstName = ref('李')
const lastName = ref('四')

const fullName = computed({
  get() {
    return `${firstName.value} ${lastName.value}`
  },
  set(newValue) {
    [firstName.value, lastName.value] = newValue.split(' ')
  }
})

// 修改示例
setTimeout(() => {
  fullName.value = '王 五' // 自动触发setter
}, 2000)
</script>

2. 链式计算

<script setup>
import { ref, computed } from 'vue'

const basePrice = ref(100)
const taxRate = ref(0.1)

// 计算含税价
const tax = computed(() => basePrice.value * taxRate.value)
const totalPrice = computed(() => basePrice.value + tax.value)
</script>

四、与 methods 的对比

特性 computed methods
缓存机制 ✅ 自动缓存 ❌ 每次调用执行
模板调用方式 属性访问 方法调用
响应式依赖追踪 ✅ 自动 ❌ 手动管理
适用场景 纯计算逻辑 需要主动触发的操作

五、实战案例

1. 购物车计算(多级依赖)

<script setup>
import { ref, computed } from 'vue'

const cartItems = ref([
  { name: '商品A', price: 199, quantity: 2 },
  { name: '商品B', price: 299, quantity: 1 }
])

// 商品总数
const totalQuantity = computed(() => 
  cartItems.value.reduce((sum, item) => sum + item.quantity, 0)
)

// 总金额(含运费)
const subtotal = computed(() => 
  cartItems.value.reduce((sum, item) => sum + item.price * item.quantity, 0)
)
const shippingFee = computed(() => subtotal.value > 500 ? 0 : 30)
const grandTotal = computed(() => subtotal.value + shippingFee.value)
</script>

2. 数据格式化

<script setup>
import { ref, computed } from 'vue'

const timestamp = ref(1677628800000)

// 日期格式化
const formattedDate = computed(() => {
  const date = new Date(timestamp.value)
  return `${date.getFullYear()}-${date.getMonth()+1}-${date.getDate()}`
})
</script>

六、最佳实践

  1. 保持纯函数
    避免在计算属性中修改外部状态或执行异步操作
  2. 命名规范
    使用形容词(如 sortedList)或名词短语(如 totalPrice
  3. 性能优化
    对于复杂计算,优先使用 computed 替代 methods
  4. 避免副作用
    不要在 getter 中修改依赖数据,可能导致无限循环
  5. 组合使用
    可将多个简单计算属性组合成复杂计算逻辑

七、常见问题

Q:何时该用 watch 替代 computed?
A:当需要执行异步操作或复杂副作用时使用 watch,纯计算场景用 computed

Q:computed 可以依赖其他 computed 吗?
A:可以,Vue 会自动建立依赖链(参见示例中的链式计算)

Q:为什么我的计算属性没有更新?
A:检查依赖项是否为响应式数据,确保通过 .value 访问 ref 值