Vue中的計(jì)算屬性與監(jiān)聽屬性
一、為什么要使用計(jì)算屬性
什么是計(jì)算屬性
計(jì)算屬性:可以理解為能夠在里面寫一些計(jì)算邏輯的屬性。具有如下的作用:
- 減少模板中的計(jì)算邏輯。
- 數(shù)據(jù)緩存。當(dāng)我們的數(shù)據(jù)沒有變化的時(shí)候,不會(huì)再次執(zhí)行計(jì)算的過(guò)程。
- 依賴固定的數(shù)據(jù)類型(響應(yīng)式數(shù)據(jù)),不能是普通的傳入的一個(gè)全局?jǐn)?shù)據(jù)。
在數(shù)據(jù)量比較大的時(shí)候,計(jì)算屬性可以幫助我們提高性能,因?yàn)橛?jì)算屬性只會(huì)在數(shù)據(jù)變化的時(shí)候才會(huì)計(jì)算。
在講解計(jì)算屬性之前先來(lái)看下面的一個(gè)例子:
需求:外賣套餐A每份15元,客戶點(diǎn)了3份,總價(jià)打八折,配送費(fèi)5元,要求在界面顯示總價(jià),代碼如下:
<template>
<div>
<div>您購(gòu)買了{(lán){info.name}}共{{info.count}}份</div>
<h1>總價(jià):{{info.count*info.price*info.sale+info.freight}}元</h1>
</div>
</template>
<script>
export default {
name:'Test',
data(){
return{
info:{
userId:1,
price:15,
name:'套餐A',
count:3,
sale:0.8,
freight:5
}
}
}
}
</script>界面運(yùn)行效果:

看了上面的例子,可能有人會(huì)問:使用這種方式已經(jīng)實(shí)現(xiàn)了需求,那為什么還要使用計(jì)算屬性呢?我們知道,vue中模板內(nèi)的表達(dá)式非常便利,設(shè)計(jì)的初衷是用于簡(jiǎn)單運(yùn)算的。如果在模板中放入太多的邏輯會(huì)讓模板過(guò)重而且難以維護(hù),看上面的代碼:
<h1>總價(jià):{{info.count*info.price*info.sale+info.freight}}元</h1>在這段代碼中,模板不在是簡(jiǎn)單的聲明式邏輯,而是復(fù)雜的邏輯計(jì)算,如果想要在多處引用總價(jià)的時(shí)候,就會(huì)難以維護(hù)。所以,對(duì)于任何復(fù)雜的邏輯,都應(yīng)當(dāng)使用計(jì)算屬性。
看下面使用計(jì)算屬性的例子:
<template>
<div>
<h1>計(jì)算屬性</h1>
<div>您購(gòu)買了{(lán){info.name}}共{{info.count}}份</div>
<!--使用計(jì)算屬性:和綁定普通屬性一樣-->
<h1>總價(jià):{{totalPrice}}元</h1>
</div>
</template>
<script>
export default {
name:'ComputedDemo',
data(){
return{
info:{
userId:1,
price:15,
name:'套餐A',
count:3,
sale:0.8,
freight:5
}
}
},
computed:{
// 定義計(jì)算屬性totalPrice
totalPrice:function(){
return this.info.count*this.info.price*this.info.sale+this.info.freight
}
}
}
</script>界面顯示效果:

注意:計(jì)算屬性是一個(gè)屬性,不是方法,不能寫在methods中,放在computed屬性里面。
上面計(jì)算屬性的寫法也可以使用ES6的寫法:
// 使用ES6寫法
totalPrice(){
return this.info.count*this.info.price*this.info.sale+this.info.freight
}二、計(jì)算屬性和方法的區(qū)別
1、區(qū)別
上面的例子除了使用計(jì)算屬性,還可以使用方法實(shí)現(xiàn):
<template>
<div>
<h1>計(jì)算屬性</h1>
<div>您購(gòu)買了{(lán){info.name}}共{{info.count}}份</div>
<!--使用計(jì)算屬性:和綁定普通屬性一樣-->
<h1>使用計(jì)算屬性獲取總價(jià):{{totalPrice}}元</h1>
<h1>使用方法獲取總價(jià):{{getTotalPrice()}}元</h1>
</div>
</template>
<script>
export default {
name:'ComputedDemo',
data(){
return{
info:{
userId:1,
price:15,
name:'套餐A',
count:3,
sale:0.8,
freight:5
}
}
},
computed:{
// 定義計(jì)算屬性totalPrice
// totalPrice:function(){
// return this.info.count*this.info.price*this.info.sale+this.info.freight;
// }
// 使用ES6寫法
totalPrice(){
return this.info.count*this.info.price*this.info.sale+this.info.freight;
}
},
methods:{
getTotalPrice(){
return this.info.count*this.info.price*this.info.sale+this.info.freight;
}
}
}
</script>界面顯示效果:

通過(guò)上面的例子可以看出:計(jì)算屬性和方法實(shí)現(xiàn)的最終效果是相同的。那么計(jì)算屬性和方法有什么區(qū)別呢?計(jì)算屬性是基于它們的響應(yīng)式依賴進(jìn)行緩存的,只有在響應(yīng)式依賴發(fā)生改變時(shí)才會(huì)重新求值。這就意味著只要響應(yīng)式依賴沒有發(fā)生改變,多次訪問計(jì)算屬性會(huì)立即返回之前的計(jì)算結(jié)果,而不必再次執(zhí)行計(jì)算。相比之下,調(diào)用方法總會(huì)再次執(zhí)行函數(shù)。總價(jià)計(jì)算屬性和方法的區(qū)別如下:
- 計(jì)算屬性在依賴發(fā)生改變時(shí)會(huì)自動(dòng)改變,而方法在依賴發(fā)生改變時(shí)需要觸發(fā)才會(huì)改變。
- 計(jì)算屬性在依賴發(fā)生改變時(shí)才會(huì)重新計(jì)算,而方法在每次調(diào)用時(shí)都會(huì)執(zhí)行。
看下面的例子:
<template>
<div>
<h1>計(jì)算屬性</h1>
<!-- <div>您購(gòu)買了{(lán){info.name}}共{{info.count}}份</div> -->
<!-- 使用計(jì)算屬性:和綁定普通屬性一樣 -->
您購(gòu)買了<input type="text" v-model="info.name" />
數(shù)量<input type="text" v-model="info.count" />
<h1>使用計(jì)算屬性獲取總價(jià):{{totalPrice}}元</h1>
<button @click="getTotalPrice">計(jì)算屬性</button>
<h1>使用方法獲取總價(jià):{{data}}元</h1>
</div>
</template>
<script>
export default {
name:'ComputedDemo',
data(){
return{
info:{
userId:1,
price:15,
name:'套餐A',
count:3,
sale:0.8,
freight:5
},
data:0
}
},
computed:{
// 定義計(jì)算屬性totalPrice
// totalPrice:function(){
// return this.info.count*this.info.price*this.info.sale+this.info.freight;
// }
// 使用ES6寫法
totalPrice(){
console.log('計(jì)算屬性');
return this.info.count*this.info.price*this.info.sale+this.info.freight;
}
},
methods:{
getTotalPrice(){
console.log('方法');
this.data= this.info.count*this.info.price*this.info.sale+this.info.freight;
}
}
}
</script>當(dāng)依賴發(fā)生改變時(shí)會(huì)多次打印“計(jì)算屬性”,而方法需要在點(diǎn)擊按鈕的時(shí)候才會(huì)發(fā)生改變。依賴不發(fā)生改變時(shí)點(diǎn)擊按鈕,也會(huì)打印“方法”。如下圖所示:

2、計(jì)算屬性使用場(chǎng)景
假如我們有一個(gè)性能開銷比較大的計(jì)算屬性A,它需要遍歷一個(gè)巨大的數(shù)組并做大量的計(jì)算,然后我們可能有其他的計(jì)算屬性依賴于計(jì)算屬性A。如果不使用計(jì)算屬性,那么將不可避免的多次進(jìn)行計(jì)算,會(huì)消耗很大的性能,這種情況下就需要使用計(jì)算屬性。
三、修改計(jì)算屬性的值
在上面的例子中都是使用的獲取后的計(jì)算屬性的值,那么如何修改計(jì)算屬性的值呢?看下面的例子:
<template>
<div>
<h1>修改計(jì)算屬性</h1>
<h2>num:{{num}}</h2>
<h2>計(jì)算屬性num2:{{num2}}</h2>
<button @click="change">改變計(jì)算屬性的值</button>
</div>
</template>
<script>
export default {
name:'ComputedDemo2',
data(){
return{
num:100
}
},
computed:{
num2(){
return this.num-10;
}
},
methods:{
change(){
this.num2=60;
}
}
}
</script>效果:

這時(shí)會(huì)發(fā)現(xiàn)直接修改計(jì)算屬性的值報(bào)錯(cuò)了,因?yàn)椴荒苤苯有薷挠?jì)算屬性的值,如果要修改計(jì)算屬性的值,需要修改其依賴項(xiàng)的值,看下面的代碼:
<template>
<div>
<h1>修改計(jì)算屬性</h1>
<h2>num:{{num}}</h2>
<h2>計(jì)算屬性num2:{{num2}}</h2>
<button @click="change">改變計(jì)算屬性的值</button>
</div>
</template>
<script>
import { get } from 'http';
export default {
name:'ComputedDemo2',
data(){
return{
num:100
}
},
computed:{
num2:{
// 當(dāng)計(jì)算屬性要修改時(shí)先觸發(fā)set方法
// 讀取當(dāng)前計(jì)算屬性中的值,get方法可以隱藏,默認(rèn)進(jìn)入的是get方法
get:function(){
return this.num-10;
},
set:function(val){
this.num=val;
}
}
},
methods:{
change(){
// 計(jì)算屬性不能直接修改
this.num2=60;
}
}
}
</script>修改前的效果:

修改后的效果:

總結(jié)
計(jì)算屬性的值不能修改,如果要修改計(jì)算屬性的值,要通過(guò)計(jì)算屬性里面的set方法修改其依賴項(xiàng)的值才能修改計(jì)算屬性的值。
四、監(jiān)聽屬性
監(jiān)聽屬性(watch)是用來(lái)監(jiān)聽data中的數(shù)據(jù)是否發(fā)生變化,一般是監(jiān)聽data中的某個(gè)屬性。
- 更加靈活、通用的API。
- watch中可以執(zhí)行任何邏輯,如函數(shù)節(jié)流,Ajax異步獲取數(shù)據(jù),甚至操作DOM。
1、監(jiān)聽普通屬性
看下面的代碼:
<template>
<div>
<h1>監(jiān)聽屬性</h1>
姓名:<input type="text" v-model="userName"/>
<h1>{{userName}}</h1>
年齡:<input type="text" v-model="age"/>
<h1>{{age}}</h1>
</div>
</template>
<script>
export default {
name:'watchDemo',
data(){
return{
userName:"abc",
age:23
}
},
methods:{
change(){
}
},
watch:{
// 監(jiān)聽userName的變化
// 有兩個(gè)參數(shù),newValue表示變化后的值,oldValue表示變化前的值
userName:function(newValue,oldValue){
console.log('修改前的值:'+oldValue);
console.log('修改后的值:'+newValue);
},
// 監(jiān)聽age的變化
age:function(newValue,oldValue){
console.log('修改前的值:'+oldValue);
console.log('修改后的值:'+newValue);
}
}
}
</script>界面效果:

2、監(jiān)聽屬性和計(jì)算屬性的區(qū)別
監(jiān)聽屬性和計(jì)算屬性的區(qū)別主要有下面幾點(diǎn):
計(jì)算屬性性能更優(yōu)。一個(gè)監(jiān)聽屬性只能監(jiān)聽一個(gè)屬性的變化,如果要同時(shí)監(jiān)聽多個(gè),就要寫多個(gè)監(jiān)聽屬性,而計(jì)算屬性可以同時(shí)監(jiān)聽多個(gè)數(shù)據(jù)的變化。監(jiān)聽屬性可以獲取改變之前的屬性值。計(jì)算屬性能做的,watch都能做,反之則不行。能用計(jì)算屬性盡量用計(jì)算屬性。
需求:userName或age改變的時(shí)候打印出當(dāng)前的userName和age值。
用監(jiān)聽屬性實(shí)現(xiàn):
<template>
<div>
<h1>監(jiān)聽屬性</h1>
姓名:<input type="text" v-model="userName"/>
<h1>{{userName}}</h1>
年齡:<input type="text" v-model="age"/>
<h1>{{age}}</h1>
<!--打印userName和age的值-->
<h1>{{info}}</h1>
</div>
</template>
<script>
export default {
name:'watchDemo',
data(){
return{
userName:"abc",
age:23,
info:''
}
},
methods:{
change(){
}
},
watch:{
// 監(jiān)聽userName的變化
// 有兩個(gè)參數(shù),newValue表示變化后的值,oldValue表示變化前的值
userName:function(newValue,oldValue){
// console.log('修改前的值:'+oldValue);
// console.log('修改后的值:'+newValue);
this.info= '我的姓名:'+ this.userName+',年齡:'+this.age;
},
// 監(jiān)聽age的變化
age:function(newValue,oldValue){
// console.log('修改前的值:'+oldValue);
// console.log('修改后的值:'+newValue);
this.info= '我的姓名:'+ this.userName+',年齡:'+this.age;
}
}
}
</script>如果要實(shí)現(xiàn)上述的需求,則需要對(duì)userName和age都進(jìn)行監(jiān)聽,監(jiān)聽屬性里面的代碼都是重復(fù)的,如果有多個(gè),那么就要寫多個(gè)監(jiān)聽屬性。在看計(jì)算屬性:
<template>
<div>
<h1>監(jiān)聽屬性</h1>
姓名:<input type="text" v-model="userName"/>
<h1>{{userName}}</h1>
年齡:<input type="text" v-model="age"/>
<h1>{{age}}</h1>
<!--打印userName和age的值-->
<!-- <h1>{{info}}</h1> -->
<!--使用計(jì)算屬性-->
<h1>{{getUserInfo}}</h1>
</div>
</template>
<script>
export default {
name:'watchDemo',
data(){
return{
userName:"abc",
age:23,
info:''
}
},
methods:{
change(){
}
},
// watch:{
// // 監(jiān)聽userName的變化
// // 有兩個(gè)參數(shù),newValue表示變化后的值,oldValue表示變化前的值
// userName:function(newValue,oldValue){
// // console.log('修改前的值:'+oldValue);
// // console.log('修改后的值:'+newValue);
// this.info= '我的姓名:'+ this.userName+',年齡:'+this.age;
// },
// // 監(jiān)聽age的變化
// age:function(newValue,oldValue){
// // console.log('修改前的值:'+oldValue);
// // console.log('修改后的值:'+newValue);
// this.info= '我的姓名:'+ this.userName+',年齡:'+this.age;
// }
// }
computed:{
getUserInfo(){
return '我的姓名:'+ this.userName+',年齡:'+this.age;
}
}
}
</script>如果使用計(jì)算屬性則只需要寫一次就可以實(shí)現(xiàn)上面的需求了。
3、監(jiān)聽復(fù)雜對(duì)象
上面的例子中是監(jiān)聽的普通屬性,那么如何監(jiān)聽對(duì)象里面的屬性呢?看下面的代碼:
<template>
<div>
<h1>監(jiān)聽屬性</h1>
姓名:<input type="text" v-model="userName"/>
<h1>{{userName}}</h1>
年齡:<input type="text" v-model="age"/>
<h1>{{age}}</h1>
<!--打印userName和age的值-->
<!-- <h1>{{info}}</h1> -->
<!--使用計(jì)算屬性-->
<h1>{{getUserInfo}}</h1>
<!--監(jiān)聽對(duì)象屬性-->
<h1>監(jiān)聽對(duì)象屬性</h1>
姓名:<input type="text" v-model="obj.name"/>
<h1>{{obj.name}}</h1>
</div>
</template>
<script>
export default {
name:'watchDemo',
data(){
return{
userName:"abc",
age:23,
info:'',
// 對(duì)象
obj:{
name:'123'
}
}
},
methods:{
change(){
}
},
watch:{
// 監(jiān)聽userName的變化
// 有兩個(gè)參數(shù),newValue表示變化后的值,oldValue表示變化前的值
userName:function(newValue,oldValue){
// console.log('修改前的值:'+oldValue);
// console.log('修改后的值:'+newValue);
this.info= '我的姓名:'+ this.userName+',年齡:'+this.age;
},
// 監(jiān)聽age的變化
age:function(newValue,oldValue){
// console.log('修改前的值:'+oldValue);
// console.log('修改后的值:'+newValue);
this.info= '我的姓名:'+ this.userName+',年齡:'+this.age;
},
// 監(jiān)聽對(duì)象中屬性的變化
'obj.name':function(newValue,oldValue){
console.log('修改前的值:'+oldValue);
console.log('修改后的值:'+newValue);
}
},
computed:{
getUserInfo(){
return '我的姓名:'+ this.userName+',年齡:'+this.age;
}
}
}
</script>效果:

能不能執(zhí)行監(jiān)聽對(duì)象呢?答案是可以的,看下面代碼:
<template>
<div>
<h1>監(jiān)聽屬性</h1>
姓名:<input type="text" v-model="userName"/>
<h1>{{userName}}</h1>
年齡:<input type="text" v-model="age"/>
<h1>{{age}}</h1>
<!--打印userName和age的值-->
<!-- <h1>{{info}}</h1> -->
<!--使用計(jì)算屬性-->
<h1>{{getUserInfo}}</h1>
<!--監(jiān)聽對(duì)象屬性-->
<h1>監(jiān)聽對(duì)象屬性</h1>
姓名:<input type="text" v-model="obj.name"/>
<h1>{{obj.name}}</h1>
<!--監(jiān)聽對(duì)象-->
<h1>監(jiān)聽對(duì)象</h1>
姓名:<input type="text" v-model="obj.name"/>
<h1>{{obj.name}}</h1>
</div>
</template>
<script>
export default {
name:'watchDemo',
data(){
return{
userName:"abc",
age:23,
info:'',
// 對(duì)象
obj:{
name:'123'
}
}
},
methods:{
change(){
}
},
watch:{
// 監(jiān)聽userName的變化
// 有兩個(gè)參數(shù),newValue表示變化后的值,oldValue表示變化前的值
userName:function(newValue,oldValue){
// console.log('修改前的值:'+oldValue);
// console.log('修改后的值:'+newValue);
this.info= '我的姓名:'+ this.userName+',年齡:'+this.age;
},
// 監(jiān)聽age的變化
age:function(newValue,oldValue){
// console.log('修改前的值:'+oldValue);
// console.log('修改后的值:'+newValue);
this.info= '我的姓名:'+ this.userName+',年齡:'+this.age;
},
// 監(jiān)聽對(duì)象中屬性的變化
// 'obj.name':function(newValue,oldValue){
// console.log('修改前的值:'+oldValue);
// console.log('修改后的值:'+newValue);
// }
// 直接監(jiān)聽對(duì)象
obj:{
// handler表示默認(rèn)執(zhí)行的函數(shù)
handler(newValue,oldValue){
console.log('修改前的值:')
console.log(oldValue);
console.log('修改后的值:');
console.log(newValue);
},
// 表示深度監(jiān)聽
// true:表示handler函數(shù)會(huì)執(zhí)行
// false:表示handler函數(shù)不會(huì)執(zhí)行
deep:true
}
},
computed:{
getUserInfo(){
return '我的姓名:'+ this.userName+',年齡:'+this.age;
}
}
}
</script>效果:

GitHub代碼地址:https://github.com/JiangXiaoLiang1988/computed.git
以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
相關(guān)文章
一次Vue中computed沒有觸發(fā)的原因以及排查經(jīng)歷
這篇文章主要介紹了一次Vue中computed沒有觸發(fā)的原因以及排查經(jīng)歷,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-11-11
詳解iview的checkbox多選框全選時(shí)校驗(yàn)問題
這篇文章主要介紹了詳解iview的checkbox多選框全選時(shí)校驗(yàn)問題,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2019-06-06
vue3+vant4封裝日期時(shí)間組件方式(年月日時(shí)分秒)
這篇文章主要介紹了vue3+vant4封裝日期時(shí)間組件方式(年月日時(shí)分秒),具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-10-10
vue3+ts+vite2項(xiàng)目實(shí)戰(zhàn)踩坑記錄
最近嘗試上手Vue3+TS+Vite,對(duì)比起Vue2有些不適應(yīng),但還是真香,下面這篇文章主要給大家介紹了關(guān)于vue3+ts+vite2項(xiàng)目的相關(guān)資料,文中通過(guò)示例代碼介紹的非常詳細(xì),需要的朋友可以參考下2022-09-09
vue2.x element-ui實(shí)現(xiàn)pc端購(gòu)物車頁(yè)面demo
這篇文章主要為大家介紹了vue2.x element-ui實(shí)現(xiàn)pc端購(gòu)物車頁(yè)面demo,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-06-06
Vue實(shí)現(xiàn)調(diào)節(jié)窗口大小時(shí)觸發(fā)事件動(dòng)態(tài)調(diào)節(jié)更新組件尺寸的方法
今天小編就為大家分享一篇Vue實(shí)現(xiàn)調(diào)節(jié)窗口大小時(shí)觸發(fā)事件動(dòng)態(tài)調(diào)節(jié)更新組件尺寸的方法。具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2018-09-09
詳解如何使用vue實(shí)現(xiàn)頁(yè)面訪問攔截
這篇文章主要為大家詳細(xì)介紹了如何使用vue實(shí)現(xiàn)頁(yè)面訪問攔截功能,文中的示例代碼講解詳細(xì),具有一定的參考價(jià)值,需要的可以了解一下2023-08-08
多個(gè)Vue項(xiàng)目部署到服務(wù)器的步驟記錄
這篇文章主要給大家介紹了關(guān)于多個(gè)Vue項(xiàng)目部署到服務(wù)器的相關(guān)資料,文中通過(guò)圖文介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2020-10-10

