Vue基础教程(60)计算属性的getter和setter方法:Vue计算属性揭秘:getter和setter的双面人生,让你的代码既聪明又贴心!

一、 为什么你的Vue代码需要计算属性?

想象一下:你开了一家在线咖啡店,页面上需要展示一杯拿铁的总价。单价30元,用户可以选择数量,还要根据数量显示折扣——买3杯以上打9折。用methods实现的话,模板里可能要塞满calculateTotalPrice()getDiscount()这类函数调用,不仅看起来乱,还可能触发不必要的重复计算。

这时候,Vue的计算属性(computed)就像个贴心小管家!它自动追踪依赖的数据(比如单价、数量),只有当这些数据变化时才会重新计算,否则直接缓存上次结果。比如下面这个经典场景:

<template>
  <div>
    <p>单价:{{ price }}元</p>
    <p>数量:<input v-model.number="quantity"></p>
    <p>总价:{{ totalPrice }}元</p> <!-- 看这里! -->
  </div>
</template>
<script>
export default {
  data() {
    return {
      price: 30,
      quantity: 1
    }
  },
  computed: {
    totalPrice() {
      console.log('重新计算总价') // 只有quantity变化时才会打印
      return this.price * this.quantity
    }
  }
}
</script>

当你反复修改数量时,控制台只会在一开始打印一次——这就是计算属性的缓存魔法!相比methods每次渲染都执行,computed在性能上直接碾压。


二、 解剖计算属性:getter是乖宝宝,setter是叛逆少年?

大部分人只用过计算属性的默认写法,比如totalPrice() { return ... }。其实这是计算属性的简写形式,完整形态包含gettersetter两个方法:

1. getter——人见人爱的优等生

getter负责读取数据,当你模板中调用{{ totalPrice }}时,其实就是触发了getter方法。它必须返回一个值,而且默认情况下就是个纯纯的getter。

实战场景:用户输入全名,自动拆分成姓和名

<template>
  <div>
    <input v-model="fullName" placeholder="输入全名(如:张三)">
    <p>姓:{{ lastName }}</p>
    <p>名:{{ firstName }}</p>
  </div>
</template>
<script>
export default {
  data() {
    return {
      lastName: '',
      firstName: ''
    }
  },
  computed: {
    fullName: {
      // getter - 当读取fullName时组合数据
      get() {
        return `${this.lastName}${this.firstName}`
      },
      // setter - 当修改fullName时拆分数据
      set(newValue) {
        // 偷偷告诉你,中文名一般是姓1个字,名2个字
        const names = newValue.split('')
        this.lastName = names[0] || ''
        this.firstName = names.slice(1).join('') || ''
      }
    }
  }
}
</script>

试试在输入框打字,你会发现姓和名自动拆分好了!这就是setter在幕后偷偷干活。

2. setter——被低估的变形金刚

setter在修改计算属性时触发,接收一个新值作为参数。它的魔法在于能够反向更新其依赖的data数据!

进阶场景:购物车价格联动

<template>
  <div>
    <p>单价:{{ price }}元</p>
    <p> 
      总价:<input v-model="finalPrice">元
      <span>({{ quantity }}件)</span>
    </p>
  </div>
</template>
<script>
export default {
  data() {
    return {
      price: 30,
      quantity: 1
    }
  },
  computed: {
    finalPrice: {
      get() {
        return this.price * this.quantity
      },
      set(newPrice) {
        // 根据总价反推数量,确保是整数
        this.quantity = Math.max(1, Math.round(newPrice / this.price))
      }
    }
  }
}
</script>

现在你可以直接修改总价输入框,数量会自动调整!这种双向联动用普通方法实现要写一堆代码,而setter几行就搞定了。


三、 实战全家桶:这些场景让你直呼“真香”

场景1:智能搜索过滤
<template>
  <div>
    <input v-model="searchKeyword" placeholder="搜索用户">
    <ul>
      <li v-for="user in filteredUsers" :key="user.id">
        {{ user.name }} - {{ user.email }}
      </li>
    </ul>
  </div>
</template>
<script>
export default {
  data() {
    return {
      searchKeyword: '',
      users: [
        { id: 1, name: '张三', email: 'zhang@email.com' },
        { id: 2, name: '李四', email: 'li@email.com' }
      ]
    }
  },
  computed: {
    filteredUsers() {
      const keyword = this.searchKeyword.toLowerCase()
      return this.users.filter(user => 
        user.name.toLowerCase().includes(keyword) ||
        user.email.toLowerCase().includes(keyword)
      )
    }
  }
}
</script>
场景2:表单验证全家桶
<template>
  <form @submit="submitForm">
    <input v-model="userInfo.username" placeholder="用户名">
    <span v-if="!validations.isUsernameValid" style="color:red">
      用户名3-10位字符
    </span>
    
    <input v-model="userInfo.age" type="number" placeholder="年龄">
    <span v-if="!validations.isAgeValid" style="color:red">
      年龄必须成年
    </span>
    
    <button :disabled="!validations.isFormValid">提交</button>
  </form>
</template>
<script>
export default {
  data() {
    return {
      userInfo: {
        username: '',
        age: 0
      }
    }
  },
  computed: {
    validations() {
      return {
        isUsernameValid: this.userInfo.username.length >= 3 && 
                         this.userInfo.username.length <= 10,
        isAgeValid: this.userInfo.age >= 18,
        isFormValid: this.isUsernameValid && this.isAgeValid
      }
    }
  }
}
</script>

四、 避坑指南:setter不是万能的!

  1. 不要直接修改getter中的依赖数据
// 错误示范!
computed: {
  fullName: {
    get() {
      // 不要在getter里修改依赖的数据
      this.firstName = '默认名' // 大忌!
      return `${this.lastName}${this.firstName}`
    }
  }
}
  1. setter中避免无限循环
computed: {
  finalPrice: {
    get() { return this.price * this.quantity },
    set(newPrice) {
      // 注意:这里修改quantity会触发finalPrice重新计算
      this.quantity = Math.round(newPrice / this.price)
      // 但不会导致无限循环,因为Vue有依赖追踪机制
    }
  }
}
  1. 复杂业务逻辑建议拆分成多个计算属性
// 好习惯:一个计算属性只负责一个功能
computed: {
  basePrice() { return this.price * this.quantity },
  discount() { return this.quantity > 3 ? 0.9 : 1 },
  finalPrice() { return this.basePrice * this.discount }
}

五、 Vue 3组合式API写法(附赠彩蛋)

如果你已经在用Vue 3,计算属性可以写得更加优雅:

<template>
  <div>
    <input v-model="fullName" placeholder="输入全名">
    <p>姓:{{ lastName }}</p>
    <p>名:{{ firstName }}</p>
  </div>
</template>
<script setup>
import { ref, computed } from 'vue'

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

const fullName = computed({
  get: () => `${lastName.value}${firstName.value}`,
  set: (newValue) => {
    const names = newValue.split('')
    lastName.value = names[0] || ''
    firstName.value = names.slice(1).join('') || ''
  }
})
</script>

六、 总结:什么时候该请出getter/setter这对神仙组合?

  • 只用getter:大多数场景,只需要根据数据计算显示值
  • 启用setter:需要双向数据绑定、反向更新数据源、处理用户输入直接修改计算结果时

记住这个秘诀:getter让数据变聪明,setter让数据变灵活。下次当你需要让数据“既能读又能写”时,别犹豫,请果断祭出计算属性的完整形态!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

值引力

持续创作,多谢支持!

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值