Vue 中的计算属性(computed)

Vue 中的计算属性(computed)

概述

计算属性是 Vue 中一个强大的特性,用于声明式地处理响应式数据的复杂逻辑。它基于 Vue 实例的响应式数据进行计算,并缓存计算结果,只有在依赖的响应式数据发生变化时才会重新计算。

基本用法

1. 定义计算属性

javascript 复制代码
new Vue({
  data() {
    return {
      firstName: '张',
      lastName: '三',
      quantity: 2,
      price: 100
    }
  },
  computed: {
    // 基本语法 - 计算属性的 getter
    fullName() {
      return this.firstName + ' ' + this.lastName
    },
    
    // 依赖多个数据源
    totalPrice() {
      return this.quantity * this.price
    }
  }
})

2. 在模板中使用

html 复制代码
<template>
  <div>
    <p>姓名:{{ fullName }}</p>
    <p>总价:{{ totalPrice }}</p>
  </div>
</template>

主要特性

1. 缓存机制

计算属性会缓存计算结果,只有当依赖的响应式数据发生变化时才会重新计算。

javascript 复制代码
computed: {
  // 这个计算属性会缓存结果
  now() {
    return Date.now() // ❌ 错误示例:实际上不会更新,因为不依赖响应式数据
  },
  
  // 正确示例:依赖响应式数据
  reversedMessage() {
    return this.message.split('').reverse().join('')
  }
}

2. Getter 和 Setter

计算属性默认只有 getter,但也可以提供 setter。

javascript 复制代码
computed: {
  fullName: {
    // getter
    get() {
      return this.firstName + ' ' + this.lastName
    },
    // setter
    set(newValue) {
      const names = newValue.split(' ')
      this.firstName = names[0]
      this.lastName = names[names.length - 1]
    }
  }
}

// 使用 setter
this.fullName = '李 四' // 会自动更新 firstName 和 lastName

与方法的区别

计算属性 vs 方法

javascript 复制代码
// 计算属性
computed: {
  computedNow() {
    return this.message + ' ' + Date.now()
    // 只有当 message 变化时才会重新计算
  }
}

// 方法
methods: {
  methodNow() {
    return this.message + ' ' + Date.now()
    // 每次调用都会重新计算
  }
}

主要区别:

  • 计算属性有缓存,方法没有
  • 计算属性基于响应式依赖,方法不自动追踪依赖
  • 计算属性在模板中像属性一样使用,方法需要加括号调用

与侦听器(watch)的区别

适用场景对比

javascript 复制代码
// 使用计算属性 - 适合同步计算
computed: {
  fullName() {
    return this.firstName + ' ' + this.lastName
  }
}

// 使用侦听器 - 适合异步操作或副作用
watch: {
  firstName(newVal, oldVal) {
    // 执行异步操作或复杂逻辑
    this.fetchUserData(newVal)
  }
}

实际应用示例

示例1:购物车计算

javascript 复制代码
new Vue({
  data() {
    return {
      cartItems: [
        { name: '商品A', price: 100, quantity: 2 },
        { name: '商品B', price: 200, quantity: 1 },
        { name: '商品C', price: 50, quantity: 3 }
      ]
    }
  },
  computed: {
    // 计算总价
    totalPrice() {
      return this.cartItems.reduce((sum, item) => {
        return sum + (item.price * item.quantity)
      }, 0)
    },
    
    // 计算商品总数
    totalItems() {
      return this.cartItems.reduce((sum, item) => {
        return sum + item.quantity
      }, 0)
    },
    
    // 是否有折扣资格(总价超过500)
    hasDiscount() {
      return this.totalPrice > 500
    },
    
    // 折后价格
    finalPrice() {
      return this.hasDiscount ? this.totalPrice * 0.9 : this.totalPrice
    }
  }
})

示例2:列表过滤和排序

javascript 复制代码
new Vue({
  data() {
    return {
      products: [
        { name: '手机', price: 2999, category: '电子产品' },
        { name: '衣服', price: 299, category: '服装' },
        { name: '电脑', price: 5999, category: '电子产品' },
        { name: '鞋子', price: 399, category: '服装' }
      ],
      selectedCategory: '',
      sortBy: 'price',
      sortOrder: 'asc'
    }
  },
  computed: {
    // 过滤产品
    filteredProducts() {
      if (!this.selectedCategory) return this.products
      
      return this.products.filter(product => 
        product.category === this.selectedCategory
      )
    },
    
    // 排序产品
    sortedProducts() {
      return [...this.filteredProducts].sort((a, b) => {
        let result = 0
        if (a[this.sortBy] < b[this.sortBy]) result = -1
        if (a[this.sortBy] > b[this.sortBy]) result = 1
        
        return this.sortOrder === 'asc' ? result : -result
      })
    },
    
    // 价格统计
    priceStats() {
      const prices = this.filteredProducts.map(p => p.price)
      return {
        min: Math.min(...prices),
        max: Math.max(...prices),
        average: prices.reduce((a, b) => a + b, 0) / prices.length
      }
    }
  }
})

最佳实践

1. 保持纯函数

计算属性的 getter 应该是纯函数,不要有副作用。

javascript 复制代码
// ✅ 正确
computed: {
  validItems() {
    return this.items.filter(item => item.isValid)
  }
}

// ❌ 避免
computed: {
  processItems() {
    this.items.forEach(item => {
      item.processed = true // 副作用!
    })
    return this.items
  }
}

2. 避免复杂计算

如果计算过于复杂,考虑拆分成多个计算属性或使用方法。

3. 命名清晰

使用描述性的名称,反映计算属性的用途。

javascript 复制代码
// ✅ 清晰
computed: {
  isFormValid() { /* ... */ },
  formattedDate() { /* ... */ }
}

// ❌ 不清晰
computed: {
  check() { /* ... */ },
  format() { /* ... */ }
}

Vue 3 中的计算属性

在 Vue 3 的组合式 API 中:

javascript 复制代码
import { ref, computed } from 'vue'

export default {
  setup() {
    const firstName = ref('张')
    const lastName = ref('三')
    
    // 只读计算属性
    const fullName = computed(() => {
      return firstName.value + ' ' + lastName.value
    })
    
    // 可写计算属性
    const writableFullName = computed({
      get: () => firstName.value + ' ' + lastName.value,
      set: (newValue) => {
        const [first, last] = newValue.split(' ')
        firstName.value = first
        lastName.value = last
      }
    })
    
    return { fullName, writableFullName }
  }
}

常见注意事项

  1. 不要在计算属性中修改依赖的数据 - 这可能导致无限循环
  2. 计算属性不能异步 - 如果需要异步计算,考虑使用侦听器或方法
  3. 依赖追踪是自动的 - 只追踪在 getter 中实际使用的响应式属性
  4. 避免在计算属性中执行高开销操作 - 利用缓存特性优化性能

计算属性是 Vue 响应式系统的核心特性之一,合理使用可以大大简化代码逻辑,提高应用性能。

相关推荐
程琬清君2 小时前
Vue3DraggableResizable可移动范围有问题
前端·javascript·vue.js
lkbhua莱克瓦242 小时前
CSS盒子模型:网页布局的基石与艺术
前端·css·笔记·javaweb
Curvatureflight2 小时前
前端性能优化指南:从加载到交互的每一毫秒
前端·性能优化·交互
♩♬♪.2 小时前
HTML学校官网静态页面
前端·css·html
天天开心a2 小时前
Vue.js 基础教程笔记(一):Vue入门与环境搭建
前端·javascript·vue.js·笔记·前端框架
weixin_446260852 小时前
解锁 React 开发新体验!Puck - 智能可视化编辑器
前端·react.js·编辑器
hzb666662 小时前
xd_day28js原生开发-day31 day41asp.net
开发语言·前端·javascript·安全·web安全
tan 912 小时前
KaliLinux2025.4 root用户修改显示语言
linux·服务器·前端·安全
小李子呢02112 小时前
Node.js
开发语言·前端·学习·node.js