03Vue3 计算属性详解及用法

一、什么是计算属性

计算属性是Vue3中用于声明式地处理响应式数据计算的特性。它基于依赖的响应式数据自动缓存计算结果,只有当依赖变化时才会重新计算。

二、基本用法

1. 使用 computed 函数

import { ref, computed } from 'vue'

// 创建响应式数据
const count = ref(0)
const price = ref(100)

// 创建计算属性
const total = computed(() => {
  return count.value * price.value
})

// 使用计算属性
console.log(total.value) // 0
count.value = 2
console.log(total.value) // 200

2. 在组件中使用

<template>
  <div>
    <input v-model.number="count" type="number">
    <input v-model.number="price" type="number">
    <p>总价: {{ total }}</p>
    <p>含税价: {{ totalWithTax }}</p>
  </div>
</template>

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

const count = ref(1)
const price = ref(100)
const taxRate = ref(0.1)

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

// 计算含税价(依赖于另一个计算属性)
const totalWithTax = computed(() => total.value * (1 + taxRate.value))
</script>

三、计算属性的特性

1. 响应式依赖追踪

import { ref, computed, watchEffect } from 'vue'

const a = ref(1)
const b = ref(2)

const sum = computed(() => a.value + b.value)

// 当a或b变化时,sum会自动更新
watchEffect(() => {
  console.log('sum changed:', sum.value)
})

a.value = 3 // 触发更新
b.value = 4 // 触发更新

2. 缓存机制

const expensiveCalculation = computed(() => {
console.log('计算中...')
return Date.now() // 假设这是昂贵的计算
})

console.log(expensiveCalculation.value) // 输出"计算中..."并返回时间戳
console.log(expensiveCalculation.value) // 直接返回缓存值,不输出"计算中..."

四、可写的计算属性

<template>
  <div>
    <input v-model="firstName">
    <input v-model="lastName">
    <p>全名: {{ fullName }}</p>
    
    <button @click="updateName('张', '三')">设置为张三</button>
  </div>
</template>

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

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

// 可写的计算属性
const fullName = computed({
  // getter
  get() {
    return `${firstName.value} ${lastName.value}`
  },
  // setter
  set(newValue) {
    const [first, last] = newValue.split(' ')
    firstName.value = first
    lastName.value = last
  }
})

// 使用方法
function updateName(first, last) {
  fullName.value = `${first} ${last}`
}
</script>

五、计算属性 vs 方法

<template>
  <div>
    <!-- 使用计算属性(有缓存) -->
    <p>计算属性结果: {{ reversedMessage }}</p>
    <p>计算属性结果: {{ reversedMessage }}</p> <!-- 不会重新计算 -->
    
    <!-- 使用方法(每次都会执行) -->
    <p>方法结果: {{ reverseMessage() }}</p>
    <p>方法结果: {{ reverseMessage() }}</p> <!-- 每次都会执行 -->
  </div>
</template>

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

const message = ref('Hello Vue3')

// 计算属性
const reversedMessage = computed(() => {
  console.log('计算属性执行')
  return message.value.split('').reverse().join('')
})

// 方法
function reverseMessage() {
  console.log('方法执行')
  return message.value.split('').reverse().join('')
}
</script>

六、计算属性 vs 侦听器

<template>
  <div>
    <input v-model="firstName">
    <input v-model="lastName">
    
    <!-- 使用计算属性(推荐) -->
    <p>全名(计算属性): {{ fullName }}</p>
    
    <!-- 使用侦听器实现相同功能 -->
    <p>全名(侦听器): {{ watchedFullName }}</p>
  </div>
</template>

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

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

// 方案1:使用计算属性(简洁高效)
const fullName = computed(() => `${firstName.value} ${lastName.value}`)

// 方案2:使用侦听器(更复杂)
const watchedFullName = ref('')
watch([firstName, lastName], ([first, last]) => {
  watchedFullName.value = `${first} ${last}`
}, { immediate: true })
</script>

七、高级用法

1. 组合式计算属性

import { ref, computed } from 'vue'

const items = ref([
  { id: 1, name: '商品A', price: 100, quantity: 2 },
  { id: 2, name: '商品B', price: 200, quantity: 1 },
  { id: 3, name: '商品C', price: 150, quantity: 3 }
])

// 多个计算属性组合
const totalItems = computed(() => 
  items.value.reduce((sum, item) => sum + item.quantity, 0)
)

const totalPrice = computed(() =>
  items.value.reduce((sum, item) => sum + item.price * item.quantity, 0)
)

const averagePrice = computed(() =>
  totalItems.value > 0 ? totalPrice.value / totalItems.value : 0
)

2. 在组合式函数中使用

// useCart.js
import { ref, computed } from 'vue'

export function useCart() {
  const cartItems = ref([])
  
  const cartTotal = computed(() =>
    cartItems.value.reduce((sum, item) => sum + item.price * item.quantity, 0)
  )
  
  const itemCount = computed(() =>
    cartItems.value.reduce((sum, item) => sum + item.quantity, 0)
  )
  
  const hasItems = computed(() => cartItems.value.length > 0)
  
  return {
    cartItems,
    cartTotal,
    itemCount,
    hasItems
  }
}

3. 性能优化:减少不必要的计算

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

const list = ref([1, 2, 3, 4, 5])
const filterThreshold = ref(3)

// 避免在计算属性中执行昂贵的过滤操作
const filteredList = computed(() => {
  // 只有当list或filterThreshold变化时才重新计算
  return list.value.filter(item => item > filterThreshold.value)
})

// 计算属性的结果可以被多次使用而不重复计算
const listLength = computed(() => filteredList.value.length)
const listSum = computed(() => 
  filteredList.value.reduce((sum, item) => sum + item, 0)
)
</script>

八、最佳实践

  1. 优先使用计算属性:当需要基于响应式数据计算新值时

  2. 保持纯函数:计算属性的getter应该是纯函数,没有副作用

  3. 避免修改依赖:不要在计算属性中修改它的依赖

  4. 合理使用缓存:对于开销大的计算,利用计算属性的缓存特性

  5. 命名清晰:使用描述性的名称,如 userFullNametotalPrice 等

九、注意事项

  1. 异步操作:计算属性不能包含异步操作,如有需要应使用 watch 或组合式函数

  2. 副作用:避免在计算属性中执行有副作用的操作

  3. 循环依赖:避免计算属性之间的循环依赖

  4. 深度监听:计算属性自动深度追踪其依赖,无需额外配置

总结

Vue3的计算属性是处理响应式数据计算的强大工具。它通过自动的依赖追踪和智能缓存,让代码更简洁、性能更高效。在实际开发中,合理使用计算属性可以显著提升代码的可维护性和应用性能。

posted @ 2026-02-10 14:09  麻辣~香锅  阅读(9)  评论(0)    收藏  举报