一. 計算屬性
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