Vue:計算屬性

1、爲何要使用計算屬性

一、什麼是計算屬性

計算屬性:能夠理解爲可以在裏面寫一些計算邏輯的屬性。具備以下的做用:vue

  • 減小模板中的計算邏輯。
  • 數據緩存。當咱們的數據沒有變化的時候,不會再次執行計算的過程。
  • 依賴固定的數據類型(響應式數據),不能是普通的傳入的一個全局數據。

在數據量比較大的時候,計算屬性能夠幫助咱們提升性能,由於計算屬性只會在數據變化的時候纔會計算。git

在講解計算屬性以前先來看下面的一個例子:github

需求:外賣套餐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>

界面運行效果:緩存

看了上面的例子,可能有人會問:使用這種方式已經實現了需求,那爲何還要使用計算屬性呢?咱們知道,vue中模板內的表達式很是便利,設計的初衷是用於簡單運算的。若是在模板中放入太多的邏輯會讓模板太重並且難以維護,看上面的代碼:異步

<h1>總價:{{info.count*info.price*info.sale+info.freight}}元</h1>

 在這段代碼中,模板不在是簡單的聲明式邏輯,而是複雜的邏輯計算,若是想要在多處引用總價的時候,就會難以維護。因此,對於任何複雜的邏輯,都應當使用計算屬性。函數

看下面使用計算屬性的例子:性能

<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>

 界面顯示效果:this

注意:計算屬性是一個屬性,不是方法,不能寫在methods中,放在computed屬性裏面。spa

上面計算屬性的寫法也可使用ES6的寫法:

// 使用ES6寫法
totalPrice(){  return this.info.count*this.info.price*this.info.sale+this.info.freight }

2、計算屬性和方法的區別

一、區別

上面的例子除了使用計算屬性,還可使用方法實現:

<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>

 界面顯示效果:

經過上面的例子能夠看出:計算屬性和方法實現的最終效果是相同的。那麼計算屬性和方法有什麼區別呢?計算屬性是基於它們的響應式依賴進行緩存的,只有在響應式依賴發生改變時纔會從新求值。這就意味着只要響應式依賴沒有發生改變,屢次訪問計算屬性會當即返回以前的計算結果,而沒必要再次執行計算。相比之下,調用方法總會再次執行函數。總價計算屬性和方法的區別以下:

  1. 計算屬性在依賴發生改變時會自動改變,而方法在依賴發生改變時須要觸發纔會改變。
  2. 計算屬性在依賴發生改變時纔會從新計算,而方法在每次調用時都會執行。

看下面的例子:

<template>
    <div>
        <h1>計算屬性</h1>
        <!-- <div>您購買了{{info.name}}共{{info.count}}份</div> -->
        <!-- 使用計算屬性:和綁定普通屬性同樣 --> 您購買了<input type="text" v-model="info.name" /> 數量<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>

 當依賴發生改變時會屢次打印「計算屬性」,而方法須要在點擊按鈕的時候纔會發生改變。依賴不發生改變時點擊按鈕,也會打印「方法」。以下圖所示:

二、計算屬性使用場景

假如咱們有一個性能開銷比較大的計算屬性A,它須要遍歷一個巨大的數組並作大量的計算,而後咱們可能有其餘的計算屬性依賴於計算屬性A。若是不使用計算屬性,那麼將不可避免的屢次進行計算,會消耗很大的性能,這種狀況下就須要使用計算屬性。

3、修改計算屬性的值

在上面的例子中都是使用的獲取後的計算屬性的值,那麼如何修改計算屬性的值呢?看下面的例子:

<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>

 效果:

這時會發現直接修改計算屬性的值報錯了,由於不能直接修改計算屬性的值,若是要修改計算屬性的值,須要修改其依賴項的值,看下面的代碼:

<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:{ // 當計算屬性要修改時先觸發set方法
             // 讀取當前計算屬性中的值,get方法能夠隱藏,默認進入的是get方法
 get:function(){ return this.num-10; }, set:function(val){ this.num=val; } } }, methods:{ change(){ // 計算屬性不能直接修改
            this.num2=60; } } } </script>

修改前的效果:

修改後的效果:

總結

計算屬性的值不能修改,若是要修改計算屬性的值,要經過計算屬性裏面的set方法修改其依賴項的值才能修改計算屬性的值。

4、監聽屬性

監聽屬性(watch)是用來監聽data中的數據是否發生變化,通常是監聽data中的某個屬性。

  • 更加靈活、通用的API。
  • watch中能夠執行任何邏輯,如函數節流,Ajax異步獲取數據,甚至操做DOM。

一、監聽普通屬性

看下面的代碼:

<template>
    <div>
        <h1>監聽屬性</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:{ // 監聽userName的變化
        // 有兩個參數,newValue表示變化後的值,oldValue表示變化前的值
 userName:function(newValue,oldValue){ console.log('修改前的值:'+oldValue); console.log('修改後的值:'+newValue); }, // 監聽age的變化
 age:function(newValue,oldValue){ console.log('修改前的值:'+oldValue); console.log('修改後的值:'+newValue); } } } </script>

 界面效果:

二、監聽屬性和計算屬性的區別

監聽屬性和計算屬性的區別主要有下面幾點:

  1. 計算屬性性能更優。一個監聽屬性只能監聽一個屬性的變化,若是要同時監聽多個,就要寫多個監聽屬性,而計算屬性能夠同時監聽多個數據的變化。
  2. 監聽屬性能夠獲取改變以前的屬性值。
  3. 計算屬性能作的,watch都能作,反之則不行。
  4. 能用計算屬性儘可能用計算屬性。

需求:userName或age改變的時候打印出當前的userName和age值。

用監聽屬性實現:

<template>
    <div>
        <h1>監聽屬性</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:{ // 監聽userName的變化
        // 有兩個參數,newValue表示變化後的值,oldValue表示變化前的值
 userName:function(newValue,oldValue){ // console.log('修改前的值:'+oldValue);
            // console.log('修改後的值:'+newValue);
            this.info= '個人姓名:'+ this.userName+',年齡:'+this.age; }, // 監聽age的變化
 age:function(newValue,oldValue){ // console.log('修改前的值:'+oldValue);
            // console.log('修改後的值:'+newValue);
            this.info= '個人姓名:'+ this.userName+',年齡:'+this.age; } } } </script>

 若是要實現上述的需求,則須要對userName和age都進行監聽,監聽屬性裏面的代碼都是重複的,若是有多個,那麼就要寫多個監聽屬性。在看計算屬性:

<template>
    <div>
        <h1>監聽屬性</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:{
    // // 監聽userName的變化
    // // 有兩個參數,newValue表示變化後的值,oldValue表示變化前的值
    // userName:function(newValue,oldValue){
    // // console.log('修改前的值:'+oldValue);
    // // console.log('修改後的值:'+newValue);
    // this.info= '個人姓名:'+ this.userName+',年齡:'+this.age;
    // },
    // // 監聽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>

 若是使用計算屬性則只須要寫一次就能夠實現上面的需求了。

三、監聽複雜對象

上面的例子中是監聽的普通屬性,那麼如何監聽對象裏面的屬性呢?看下面的代碼:

<template>
    <div>
        <h1>監聽屬性</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>
        <!--監聽對象屬性-->
        <h1>監聽對象屬性</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:{ // 監聽userName的變化
        // 有兩個參數,newValue表示變化後的值,oldValue表示變化前的值
 userName:function(newValue,oldValue){ // console.log('修改前的值:'+oldValue);
            // console.log('修改後的值:'+newValue);
            this.info= '個人姓名:'+ this.userName+',年齡:'+this.age; }, // 監聽age的變化
 age:function(newValue,oldValue){ // console.log('修改前的值:'+oldValue);
            // console.log('修改後的值:'+newValue);
            this.info= '個人姓名:'+ this.userName+',年齡:'+this.age; }, // 監聽對象中屬性的變化
        'obj.name':function(newValue,oldValue){ console.log('修改前的值:'+oldValue); console.log('修改後的值:'+newValue); } }, computed:{ getUserInfo(){ return '個人姓名:'+ this.userName+',年齡:'+this.age; } } } </script>

 效果:

能不能執行監聽對象呢?答案是能夠的,看下面代碼:

<template>
    <div>
        <h1>監聽屬性</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>
        <!--監聽對象屬性-->
        <h1>監聽對象屬性</h1> 姓名:<input type="text" v-model="obj.name"/>
        <h1>{{obj.name}}</h1>
        <!--監聽對象-->
        <h1>監聽對象</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:{ // 監聽userName的變化
        // 有兩個參數,newValue表示變化後的值,oldValue表示變化前的值
 userName:function(newValue,oldValue){ // console.log('修改前的值:'+oldValue);
            // console.log('修改後的值:'+newValue);
            this.info= '個人姓名:'+ this.userName+',年齡:'+this.age; }, // 監聽age的變化
 age:function(newValue,oldValue){ // console.log('修改前的值:'+oldValue);
            // console.log('修改後的值:'+newValue);
            this.info= '個人姓名:'+ this.userName+',年齡:'+this.age; }, // 監聽對象中屬性的變化
        // 'obj.name':function(newValue,oldValue){
        // console.log('修改前的值:'+oldValue);
        // console.log('修改後的值:'+newValue);
        // }
        // 直接監聽對象
 obj:{ // handler表示默認執行的函數
 handler(newValue,oldValue){ console.log('修改前的值:') console.log(oldValue); console.log('修改後的值:'); console.log(newValue); }, // 表示深度監聽 
            // true:表示handler函數會執行
            // false:表示handler函數不會執行
 deep:true } }, computed:{ getUserInfo(){ return '個人姓名:'+ this.userName+',年齡:'+this.age; } } } </script>

 效果:

 GitHub代碼地址:https://github.com/JiangXiaoLiang1988/computed.git