Vue中的計算屬性與監(jiān)聽屬性
一、為什么要使用計算屬性
什么是計算屬性
計算屬性:可以理解為能夠在里面寫一些計算邏輯的屬性。具有如下的作用:
- 減少模板中的計算邏輯。
- 數(shù)據(jù)緩存。當我們的數(shù)據(jù)沒有變化的時候,不會再次執(zhí)行計算的過程。
- 依賴固定的數(shù)據(jù)類型(響應(yīng)式數(shù)據(jù)),不能是普通的傳入的一個全局數(shù)據(jù)。
在數(shù)據(jù)量比較大的時候,計算屬性可以幫助我們提高性能,因為計算屬性只會在數(shù)據(jù)變化的時候才會計算。
在講解計算屬性之前先來看下面的一個例子:
需求:外賣套餐A每份15元,客戶點了3份,總價打八折,配送費5元,要求在界面顯示總價,代碼如下:
<template> <div> <div>您購買了{{info.name}}共{{info.count}}份</div> <h1>總價:{{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>
界面運行效果:
看了上面的例子,可能有人會問:使用這種方式已經(jīng)實現(xiàn)了需求,那為什么還要使用計算屬性呢?我們知道,vue中模板內(nèi)的表達式非常便利,設(shè)計的初衷是用于簡單運算的。如果在模板中放入太多的邏輯會讓模板過重而且難以維護,看上面的代碼:
<h1>總價:{{info.count*info.price*info.sale+info.freight}}元</h1>
在這段代碼中,模板不在是簡單的聲明式邏輯,而是復(fù)雜的邏輯計算,如果想要在多處引用總價的時候,就會難以維護。所以,對于任何復(fù)雜的邏輯,都應(yīng)當使用計算屬性。
看下面使用計算屬性的例子:
<template> <div> <h1>計算屬性</h1> <div>您購買了{{info.name}}共{{info.count}}份</div> <!--使用計算屬性:和綁定普通屬性一樣--> <h1>總價:{{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:{ // 定義計算屬性totalPrice totalPrice:function(){ return this.info.count*this.info.price*this.info.sale+this.info.freight } } } </script>
界面顯示效果:
注意:計算屬性是一個屬性,不是方法,不能寫在methods中,放在computed屬性里面。
上面計算屬性的寫法也可以使用ES6的寫法:
// 使用ES6寫法 totalPrice(){ return this.info.count*this.info.price*this.info.sale+this.info.freight }
二、計算屬性和方法的區(qū)別
1、區(qū)別
上面的例子除了使用計算屬性,還可以使用方法實現(xiàn):
<template> <div> <h1>計算屬性</h1> <div>您購買了{{info.name}}共{{info.count}}份</div> <!--使用計算屬性:和綁定普通屬性一樣--> <h1>使用計算屬性獲取總價:{{totalPrice}}元</h1> <h1>使用方法獲取總價:{{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:{ // 定義計算屬性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>
界面顯示效果:
通過上面的例子可以看出:計算屬性和方法實現(xiàn)的最終效果是相同的。那么計算屬性和方法有什么區(qū)別呢?計算屬性是基于它們的響應(yīng)式依賴進行緩存的,只有在響應(yīng)式依賴發(fā)生改變時才會重新求值。這就意味著只要響應(yīng)式依賴沒有發(fā)生改變,多次訪問計算屬性會立即返回之前的計算結(jié)果,而不必再次執(zhí)行計算。相比之下,調(diào)用方法總會再次執(zhí)行函數(shù)??們r計算屬性和方法的區(qū)別如下:
- 計算屬性在依賴發(fā)生改變時會自動改變,而方法在依賴發(fā)生改變時需要觸發(fā)才會改變。
- 計算屬性在依賴發(fā)生改變時才會重新計算,而方法在每次調(diào)用時都會執(zhí)行。
看下面的例子:
<template> <div> <h1>計算屬性</h1> <!-- <div>您購買了{{info.name}}共{{info.count}}份</div> --> <!-- 使用計算屬性:和綁定普通屬性一樣 --> 您購買了<input type="text" v-model="info.name" /> 數(shù)量<input type="text" v-model="info.count" /> <h1>使用計算屬性獲取總價:{{totalPrice}}元</h1> <button @click="getTotalPrice">計算屬性</button> <h1>使用方法獲取總價:{{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:{ // 定義計算屬性totalPrice // totalPrice:function(){ // return this.info.count*this.info.price*this.info.sale+this.info.freight; // } // 使用ES6寫法 totalPrice(){ console.log('計算屬性'); 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>
當依賴發(fā)生改變時會多次打印“計算屬性”,而方法需要在點擊按鈕的時候才會發(fā)生改變。依賴不發(fā)生改變時點擊按鈕,也會打印“方法”。如下圖所示:
2、計算屬性使用場景
假如我們有一個性能開銷比較大的計算屬性A,它需要遍歷一個巨大的數(shù)組并做大量的計算,然后我們可能有其他的計算屬性依賴于計算屬性A。如果不使用計算屬性,那么將不可避免的多次進行計算,會消耗很大的性能,這種情況下就需要使用計算屬性。
三、修改計算屬性的值
在上面的例子中都是使用的獲取后的計算屬性的值,那么如何修改計算屬性的值呢?看下面的例子:
<template> <div> <h1>修改計算屬性</h1> <h2>num:{{num}}</h2> <h2>計算屬性num2:{{num2}}</h2> <button @click="change">改變計算屬性的值</button> </div> </template> <script> export default { name:'ComputedDemo2', data(){ return{ num:100 } }, computed:{ num2(){ return this.num-10; } }, methods:{ change(){ this.num2=60; } } } </script>
效果:
這時會發(fā)現(xiàn)直接修改計算屬性的值報錯了,因為不能直接修改計算屬性的值,如果要修改計算屬性的值,需要修改其依賴項的值,看下面的代碼:
<template> <div> <h1>修改計算屬性</h1> <h2>num:{{num}}</h2> <h2>計算屬性num2:{{num2}}</h2> <button @click="change">改變計算屬性的值</button> </div> </template> <script> import { get } from 'http'; export default { name:'ComputedDemo2', data(){ return{ num:100 } }, computed:{ num2:{ // 當計算屬性要修改時先觸發(fā)set方法 // 讀取當前計算屬性中的值,get方法可以隱藏,默認進入的是get方法 get:function(){ return this.num-10; }, set:function(val){ this.num=val; } } }, methods:{ change(){ // 計算屬性不能直接修改 this.num2=60; } } } </script>
修改前的效果:
修改后的效果:
總結(jié)
計算屬性的值不能修改,如果要修改計算屬性的值,要通過計算屬性里面的set方法修改其依賴項的值才能修改計算屬性的值。
四、監(jiān)聽屬性
監(jiān)聽屬性(watch)是用來監(jiān)聽data中的數(shù)據(jù)是否發(fā)生變化,一般是監(jiān)聽data中的某個屬性。
- 更加靈活、通用的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的變化 // 有兩個參數(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)聽屬性和計算屬性的區(qū)別
監(jiān)聽屬性和計算屬性的區(qū)別主要有下面幾點:
計算屬性性能更優(yōu)。一個監(jiān)聽屬性只能監(jiān)聽一個屬性的變化,如果要同時監(jiān)聽多個,就要寫多個監(jiān)聽屬性,而計算屬性可以同時監(jiān)聽多個數(shù)據(jù)的變化。監(jiān)聽屬性可以獲取改變之前的屬性值。計算屬性能做的,watch都能做,反之則不行。能用計算屬性盡量用計算屬性。
需求:userName或age改變的時候打印出當前的userName和age值。
用監(jiān)聽屬性實現(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的變化 // 有兩個參數(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>
如果要實現(xiàn)上述的需求,則需要對userName和age都進行監(jiān)聽,監(jiān)聽屬性里面的代碼都是重復(fù)的,如果有多個,那么就要寫多個監(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> <!--打印userName和age的值--> <!-- <h1>{{info}}</h1> --> <!--使用計算屬性--> <h1>{{getUserInfo}}</h1> </div> </template> <script> export default { name:'watchDemo', data(){ return{ userName:"abc", age:23, info:'' } }, methods:{ change(){ } }, // watch:{ // // 監(jiān)聽userName的變化 // // 有兩個參數(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>
如果使用計算屬性則只需要寫一次就可以實現(xiàn)上面的需求了。
3、監(jiān)聽復(fù)雜對象
上面的例子中是監(jiān)聽的普通屬性,那么如何監(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> <!--打印userName和age的值--> <!-- <h1>{{info}}</h1> --> <!--使用計算屬性--> <h1>{{getUserInfo}}</h1> <!--監(jiān)聽對象屬性--> <h1>監(jiān)聽對象屬性</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:'', // 對象 obj:{ name:'123' } } }, methods:{ change(){ } }, watch:{ // 監(jiān)聽userName的變化 // 有兩個參數(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)聽對象中屬性的變化 'obj.name':function(newValue,oldValue){ console.log('修改前的值:'+oldValue); console.log('修改后的值:'+newValue); } }, computed:{ getUserInfo(){ return '我的姓名:'+ this.userName+',年齡:'+this.age; } } } </script>
效果:
能不能執(zhí)行監(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> <!--打印userName和age的值--> <!-- <h1>{{info}}</h1> --> <!--使用計算屬性--> <h1>{{getUserInfo}}</h1> <!--監(jiān)聽對象屬性--> <h1>監(jiān)聽對象屬性</h1> 姓名:<input type="text" v-model="obj.name"/> <h1>{{obj.name}}</h1> <!--監(jiān)聽對象--> <h1>監(jiān)聽對象</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:'', // 對象 obj:{ name:'123' } } }, methods:{ change(){ } }, watch:{ // 監(jiān)聽userName的變化 // 有兩個參數(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)聽對象中屬性的變化 // 'obj.name':function(newValue,oldValue){ // console.log('修改前的值:'+oldValue); // console.log('修改后的值:'+newValue); // } // 直接監(jiān)聽對象 obj:{ // handler表示默認執(zhí)行的函數(shù) handler(newValue,oldValue){ console.log('修改前的值:') console.log(oldValue); console.log('修改后的值:'); console.log(newValue); }, // 表示深度監(jiān)聽 // true:表示handler函數(shù)會執(zhí)行 // false:表示handler函數(shù)不會執(zhí)行 deep:true } }, computed:{ getUserInfo(){ return '我的姓名:'+ this.userName+',年齡:'+this.age; } } } </script>
效果:
GitHub代碼地址:https://github.com/JiangXiaoLiang1988/computed.git
以上就是本文的全部內(nèi)容,希望對大家的學習有所幫助,也希望大家多多支持腳本之家。
相關(guān)文章
一次Vue中computed沒有觸發(fā)的原因以及排查經(jīng)歷
這篇文章主要介紹了一次Vue中computed沒有觸發(fā)的原因以及排查經(jīng)歷,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教2023-11-11vue2.x element-ui實現(xiàn)pc端購物車頁面demo
這篇文章主要為大家介紹了vue2.x element-ui實現(xiàn)pc端購物車頁面demo,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪2023-06-06Vue實現(xiàn)調(diào)節(jié)窗口大小時觸發(fā)事件動態(tài)調(diào)節(jié)更新組件尺寸的方法
今天小編就為大家分享一篇Vue實現(xiàn)調(diào)節(jié)窗口大小時觸發(fā)事件動態(tài)調(diào)節(jié)更新組件尺寸的方法。具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧2018-09-09