一. 計算屬性
1. 什麼是計算屬性?
通常, 我們是在模板中, 通過插值語法顯示data的內容, 但有時候我們可能需要在{{}}
裡新增一些計算, 然後在展示出來資料. 這時我們可以使用到計算屬性
先來舉個例子, 比如: 一個班, 有幾個學生參加期末考試, 要計算考試的平均分. 我們來看看, 通常要怎麼做?
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <div id="app">考試成績 <ul> <li v-for="stu in students">{{stu.name}} -- {{stu.score}}</li> </ul> <p>平均分: <label>{{getAvg()}}</label></p> </div> <script src="../js/vue.js"></script> <script> const app = new Vue({ el: "#app", data: { message:"班級考試平均分", students: [ {name:"張三", score:90}, {name:"lisi", score:100}, {name:"wangwu", score:99}, {name:"zhaoliu", score:89}, {name:"liuqi", score:95} ] }, methods: { getAvg() { let sum = 0; for (let i = 0; i < this.students.length; i++) { console.log(this.students[i].score); let stu = this.students[i]; sum += stu.score; } console.log("平均分:" + sum/this.students.length); return sum/this.students.length; } } }) </script> </body> </html>
我們定義了一組學生的成績. 然後將其顯示在頁面上, 然後通過方法getAvg計算平均分.
這裡我們在獲取平均分的時候, 使用的是{{getAve()}} 其實, 平均分我們理解更像是一個屬性, 而不是一個方法. 為了方便計算, vue給我們提供了一個computed屬性, 專門用來做計算. computed中定義的也是方法, 這個方法的方法名通常都定義為名詞. 我們來看一下使用
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <div id="app">考試成績 <ul> <li v-for="stu in students">{{stu.name}} -- {{stu.score}}</li> </ul> <p>平均分: <label>{{avg}}</label></p> </div> <script src="../js/vue.js"></script> <script> const app = new Vue({ el: "#app", data: { message:"班級考試平均分", students: [ {name:"zhangsan", score:90}, {name:"lisi", score:100}, {name:"wangwu", score:99}, {name:"zhaoliu", score:89}, {name:"liuqi", score:95} ] }, computed: { avg: function() { let sum = 0; for (let i = 0; i < this.students.length; i++) { console.log(this.students[i].score); let stu = this.students[i]; sum += stu.score; } console.log("平均分:" + sum/this.students.length); return sum/this.students.length; } }, methods: { } }) </script> </body> </html>
這裡,增加了一個computed屬性, 裡面定義了avg方法, 沒錯, 本質還是方法, 但命名的時候, 將其命名為名詞.
眼尖的同學應該已經發現了, 這好像和methods方法一樣啊, 就是換了個名字. 那computed計算屬性和methods方法有什麼區別呢?
2. 計算屬性computed的快取功能
我們用案例來說明他們之間的區別.
案例1. methods方法
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <div id="app"> <p> Origin Message: {{message}}</p> <p>Mthod Message:{{getMessage()}}</p> <p>Mthod Grade:{{getGrade()}}</p> <p>Mthod Class:{{getClass()}}</p> </div> <script src="../js/vue.js"></script> <script> const app = new Vue({ el: "#app", data: { message:"班級考試平均分", className: "1班", gradeName:"一年級" }, methods: { getGrade: function(){ console.log("呼叫Grade計算") return "方法" + this.gradeName }, getClass: function(){ console.log("呼叫class計算") return "方法" + this.className }, getMessage: function(){ console.log("呼叫message計算") return "方法" + this.message } } }) </script> </body> </html>
我們發現, 在修改一個屬性, 其他屬性都沒變化的情況下, 我們發現methods裡的方法都被執行了一遍
案例2. computed計算屬性
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <div id="app"> <p> Origin Message: {{message}}</p> <p>Mthod Message:{{getMessage}}</p> <p>Mthod Grade:{{getGrade}}</p> <p>Mthod Class:{{getClass}}</p> </div> <script src="../js/vue.js"></script> <script> const app = new Vue({ el: "#app", data: { message:"班級考試平均分", className: "1班", gradeName:"一年級" }, computed: { getGrade: function(){ console.log("呼叫Grade計算") return "方法" + this.gradeName }, getClass: function(){ console.log("呼叫class計算") return "方法" + this.className }, getMessage: function(){ console.log("呼叫message計算") return "方法" + this.message } } }) </script> </body> </html>
控制檯輸出
我們發現, 當控制檯修改其中一個屬性值, 只有呼叫這個屬性的方法會重新執行
案例3: 再看一個computed快取的例子
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <div id="app"> <p>呼叫方法</p> <p>{{getMes()}}</p> <p>{{getMes()}}</p> <p>{{getMes()}}</p> <p>{{getMes()}}</p> <p>呼叫計算屬性</p> <p>{{mes}}</p> <p>{{mes}}</p> <p>{{mes}}</p> <p>{{mes}}</p> </div> <script src="../js/vue.js"></script> <script> const app = new Vue({ el: "#app", data: { firstName: "Elon", lastName: "Musk" }, computed: { mes: function(){ console.log("呼叫計算屬性") return this.firstName + " " + this.lastName } }, methods: { getMes: function(){ console.log("呼叫method方法") return this.firstName + " " + this.lastName } } }) </script> </body> </html>
這是兩種方式的呼叫, 但是結果都一樣, 都是列印輸出姓名, 計算屬性mes呼叫了四次, 方法getMes()也呼叫了四次, 我們來看看執行結果
兩次列印的結果是一樣的, 但是呼叫getMes()呼叫了4次, 而mes計算屬性只計算了一次.
總結
- methods方法和computed計算屬性,兩種方式的最終結果確實是完全相同
- 不同的是計算屬性是基於它們的響應式依賴進行快取的。只在相關響應式依賴發生改變時它們才會重新求值,多次訪問getMessage 計算屬性會立即返回之前的計算結果,而不必再次執行函式。
- methods方法,每當觸發重新渲染時,呼叫方法將總會再次執行函式。
所以,官網說,對於任何複雜邏輯,都應當使用計算屬性。
3. 計算屬性的getter和setter訪問器
問題: 我們發現, 在計算屬性和methods方法呼叫的是偶還有一點不同, 那就是呼叫方式不同. method方呼叫是{{getMessage()}}, 而計算屬性是{{getMessage}}, 我們上面不是說計算屬性中定義的也是方法麼? 為什麼不需要使用()呢? 下面來研究一下
還是這個案例, 我們來看看程式碼
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <div id="app">{{message}} {{avg}}</div> <script src="../js/vue.js"></script> <script> var app = new Vue({ el: "#app", data: { message: "計算平均分:", students: [ {name:"zhangsan", score:90}, {name:"lisi", score:100}, {name:"wangwu", score:99}, {name:"zhaoliu", score:89}, {name:"liuqi", score:95} ] }, computed: { avg: function() { let sum = 0; for (let i = 0; i < this.students.length; i++) { console.log(this.students[i].score); let stu = this.students[i]; sum += stu.score; } console.log("平均分:" + sum/this.students.length); return sum/this.students.length; } } }); </script> </body> </html>
我們在計算平均分的時候, 是把avg當做一個屬性來對待的, 所以,呼叫的時候這麼寫{{avg}}, 而不是{{avg()}}. 但是我們定義的時候卻是給定義成方法了, 為什麼會這樣呢?
下面我們來研究computed完整的寫法, 研究完這個, 就知道為什麼這麼寫了.
- 其實計算屬性本身是定義為了一個屬性. 例如: 我們定義test, 通常我們定義屬性是這麼定義的
test: "這是一個屬性"
- 在計算屬性裡, 屬性值是一個物件, 所以, 我們要這麼定義
computed: {
test: {
}
}
- 物件的內部有兩個方法, 一個是get方法, 一個是set方法. 這時在get方法中return一個abc, 這是, 在頁面顯示的就應該是abc
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <div id="app">{{message}} --- {{avg}} --- {{test}}</div> <script src="../js/vue.js"></script> <script> var app = new Vue({ el: "#app", data: { message: "計算平均分:", students: [ {name:"zhangsan", score:90}, {name:"lisi", score:100}, {name:"wangwu", score:99}, {name:"zhaoliu", score:89}, {name:"liuqi", score:95} ] }, computed: { avg: function() { let sum = 0; for (let i = 0; i < this.students.length; i++) { console.log(this.students[i].score); let stu = this.students[i]; sum += stu.score; } console.log("平均分:" + sum/this.students.length); return sum/this.students.length; }, test : { set: function(newValue) { this.message = newValue; console.log("呼叫setter") }, get: function() { return "abc" } } } }); </script> </body> </html>
看看效果
確實列印輸出了abc
- 因為有get方法和set方法, 所以, 我們可以修改test的值, 如下: 修改了app.test的值, 最終改變了message的值.
- 然而, 計算屬性通常只實現get方法, 而不實現set方法. 我們是計算後輸出, 而不允許北外不修改, 這時計算屬性就只剩下一個get方法, 最後我們將其簡寫, 去掉get, 就是我們通常看到的寫法
computed: { avg: function() { let sum = 0; for (let i = 0; i < this.students.length; i++) { console.log(this.students[i].score); let stu = this.students[i]; sum += stu.score; } console.log("平均分:" + sum/this.students.length); return sum/this.students.length; }, avg1 : function() { return "abc" } }
雖然寫法和method差不多. 但本質上, 計算屬性還是屬性, 所以, 和屬性的寫法是一樣的.
as