1. 什么是计算属性
计算属性本质上就是一个 function 函数,它可以实时监听 data 中数据的变化,并 return 一个计算后的新值,
供组件渲染 DOM 时使用。
2. 如何声明计算属性
计算属性需要以 function 函数的形式声明到组件的 computed 选项中,示例代码如下:
<!-- 直接以属性的方式使用 --> <template> {{ count }} </template> <script> export default { computed:{ <!-- 以函数的方式定义 --> count(n) { return n+2 } }) return i }, } } </script>
注意:计算属性侧重于得到一个计算的结果,因此计算属性中必须有 return 返回值!
3. 计算属性的使用注意点
① 计算属性必须定义在 computed 节点中
② 计算属性必须是一个 function 函数
③ 计算属性必须有返回值
④ 计算属性必须当做普通属性使用
4. 计算属性 vs 方法
相对于方法来说,计算属性会缓存计算的结果,只有计算属性的依赖项发生变化时,才会重新进行运算。因此
计算属性的性能更好。使用方法,在模板中使用多次会计算多次,而计算属性只计算一次,后面调用缓存。
5. 计算属性案例
案例需求,使用计算属性动态计算:
① 已勾选的商品总个数
② 已勾选的商品总价
③ 结算按钮的禁用状态
结算区域代码:
<!-- 结算区域 --> <div class="settle-box"> <!-- TODO: 1. 动态计算已勾选的商品的总数量 --> <span>总数量:{{ count }}</span> <!-- TODO: 2. 动态计算已勾选的商品的总价 --> <span>总价:{{ price }}元</span> <!-- TODO: 3. 动态计算按钮的禁用状态 --> <button type="button" style="background-color: #f40; border: 1px #f40 solid;" class="btn btn-primary" :disabled="isdisabled">结算</button> </div>
计算属性相关代码:
<script> export default { computed:{ // 已勾选商品的总数量 count(x) { let i = 0 this.fruitlist.forEach(x=>{ if(x.state === true){ i += x.count } }) return i }, // 已勾选商品的总价格 price(x) { let p = 0 this.fruitlist.forEach(x=>{ if(x.state === true){ p += x.price*x.count } }) return p }, // 结算按钮是否禁用 isdisabled(){ return this.price === 0 } }, }
完整代码-FruitList.vue:
<template> <div class="fruit-list-container"> <!-- 水果列表 --> <div class="fruit-list"> <!-- 水果的 item 项 --> <div class="fruit-item" v-for="item in fruitlist" :key="item.id"> <div class="left"> <div class="custom-control custom-checkbox"> <input type="checkbox" class="custom-control-input" :id="item.id" v-model="item.state" /> <label class="custom-control-label" :for="item.id"> <!-- 水果图片 --> <img :src="item.pic" alt="" class="thumb" /> </label> </div> </div> <div class="right"> <!-- 水果名称 --> <div class="top">{{ item.fruit }}</div> <div class="bottom"> <!-- 水果单价 --> <span class="price">¥{{ item.price }}</span> <div class="btns"> <!-- 水果数量 --> <button type="button" class="btn btn-light" @click="onSubClick(item.id)">-</button> <span class="count">{{ item.count }}</span> <button type="button" class="btn btn-light" @click="onAddClick(item.id)">+</button> </div> </div> </div> </div> </div> <!-- 结算区域 --> <div class="settle-box"> <!-- TODO: 1. 动态计算已勾选的商品的总数量 --> <span>总数量:{{ count }}</span> <!-- TODO: 2. 动态计算已勾选的商品的总价 --> <span>总价:{{ price }}元</span> <!-- TODO: 3. 动态计算按钮的禁用状态 --> <button type="button" style="background-color: #f40; border: 1px #f40 solid;" class="btn btn-primary" :disabled="isdisabled">结算</button> </div> </div> </template> <script> export default { name: 'FruitList', data() { return { fruitlist: [ { id: 1, fruit: '香橼', pic: '/src/assets/images/1.jpg', price: 5, count: 1, state: true }, { id: 2, fruit: '柚子', pic: '/src/assets/images/2.jpg', price: 4.5, count: 1, state: false }, { id: 3, fruit: '橘子', pic: '/src/assets/images/3.jpg', price: 3, count: 1, state: false }, { id: 4, fruit: '橙子', pic: '/src/assets/images/4.jpg', price: 6, count: 1, state: false }, { id: 5, fruit: '粑粑柑', pic: '/src/assets/images/5.jpg', price: 6.5, count: 1, state: false }, { id: 6, fruit: '柠檬', pic: '/src/assets/images/6.jpg', price: 4, count: 1, state: false }, { id: 7, fruit: '青柠', pic: '/src/assets/images/7.jpg', price: 5.2, count: 1, state: false }, ], } }, computed:{ // 已勾选商品的总数量 count(x) { let i = 0 this.fruitlist.forEach(x=>{ if(x.state === true){ i += x.count } }) return i }, // 已勾选商品的总价格 price(x) { let p = 0 this.fruitlist.forEach(x=>{ if(x.state === true){ p += x.price*x.count } }) return p }, // 结算按钮是否禁用 isdisabled(){ return this.price === 0 } }, methods: { // 点击了数量 -1 按钮 onSubClick(id) { const findResult = this.fruitlist.find(x => x.id === id) if (findResult && findResult.count > 1) { findResult.count-- } }, // 点击了数量 +1 按钮 onAddClick(id) { const findResult = this.fruitlist.find(x => x.id === id) if (findResult) { findResult.count++ } }, }, } </script> <style lang="less" scoped> .fruit-list-container { padding-bottom: 50px; } .fruit-item { display: flex; padding: 10px; & + .fruit-item { border-top: 1px solid #efefef; } .left { margin-right: 10px; .thumb { width: 100px; height: 100px; } } .right { display: flex; flex: 1; flex-direction: column; justify-content: space-between; .top { font-weight: bold; font-size: 13px; } .bottom { display: flex; justify-content: space-between; align-items: center; .price { font-size: 13px; font-weight: bold; color: red; } .btns { display: flex; align-items: center; .count { display: inline-block; width: 28px; text-align: center; } } } } } .custom-control-label::before, .custom-control-label::after { top: 47px; border-radius: 10px !important; } .settle-box { height: 50px; position: fixed; bottom: 0; left: 0; width: 100%; background-color: #fff; border-top: 1px solid #efefef; display: flex; align-items: center; justify-content: space-between; padding: 0 10px; } </style>
完整代码-MyHeader.vue
<template> <div class="header-container" :style="{ backgroundColor: bgcolor, color: color }"> {{title || 'Header 组件'}} </div> </template> <script> export default { name: 'MyHeader', props: ['title', 'bgcolor', 'color'] } </script> <style lang="less" scoped> .header-container { height: 45px; background-color: pink; text-align: center; line-height: 45px; position: fixed; top: 0; left: 0; width: 100%; z-index: 999; } </style>
完整代码-App.vue
<template> <div class="app-container"> <my-header title="水果列表" color="white" bgcolor="#f40"></my-header> <fruit-list></fruit-list> </div> </template> <script> import MyHeader from './components/my-header/MyHeader.vue' import FruitList from './components/fruit-list/FruitList.vue' export default { name: 'MyApp', components: { MyHeader, FruitList, }, } </script> <style lang="less" scoped> .app-container { padding-top: 45px; } </style>
完整打包源代码下载:
https://gitee.com/xingyueqianduan/vuecomputedfruit
摘自: