在Vue開發中,模板內的表達式非常便利,但是設計它們的初衷是用於簡單運算的。在模板中放入太多的邏輯會讓模板過重且難以維護。當你想要在模板中多次引用相同表達式時,就會更加難以處理。所以,對於任何複雜邏輯,你都應當使用計算屬性。本文主要講解Vue中的計算屬性和偵聽器,僅供學習分享使用,如有不足之前,還請... ...
概述
在Vue開發中,模板內的表達式非常便利,但是設計它們的初衷是用於簡單運算的。在模板中放入太多的邏輯會讓模板過重且難以維護。當你想要在模板中多次引用相同表達式時,就會更加難以處理。所以,對於任何複雜邏輯,你都應當使用計算屬性。本文主要講解Vue中的計算屬性和偵聽器,僅供學習分享使用,如有不足之前,還請指正。
計算屬性
計算屬性步驟:
1. 在computed屬性中增加reverseMsg方法,如下所示:
1 <script type="text/javascript"> 2 var vm = new Vue({ 3 el: '#app', 4 data: { 5 msg: 'welcome to vue world!!!' 6 7 }, 8 computed: { 9 reverseMsg: function() { 10 // `this` 指向 vm 實例 11 return this.msg.split('').reverse().join(''); 12 } 13 } 14 15 }); 16 </script>
2. 在Html中進行引用,你可以像綁定普通屬性一樣在模板中綁定計算屬性。
Vue 知道 vm.reverseMsg 依賴於 vm.msg,因此當 vm.msg 發生改變時,所有依賴 vm.reverseMsg 的綁定也會更新。如下所示:
1 <p>原始信息: {{ msg }}</p> 2 <p>計算屬性反轉信息: {{ reverseMsg }}</p>
採用表達式的方式 ,則是如下所示:
1 <span>{{ msg.split('').reverse().join('') }}</span>
在這個地方,模板不再是簡單的聲明式邏輯。你必須看一段時間才能意識到,這裡是想要顯示變數 msg 的翻轉字元串。此處對比一下,採用計算屬性的方式,則更加簡潔明瞭。
計算屬性緩存 vs 方法
你可能已經註意到我們可以通過在表達式中調用方法來達到同樣的效果,我們可以將同一函數定義為一個方法而不是一個計算屬性。兩種方式的最終結果確實是完全相同的。
如下所示,聲明一個方法:
1 <script type="text/javascript"> 2 var vm = new Vue({ 3 el: '#app', 4 data: { 5 msg: 'welcome to vue world!!!' 6 }, 7 methods: { 8 reversedMsg: function() { 9 return this.msg.split('').reverse().join(''); 10 } 11 } 12 13 }); 14 </script>
在Html中進行引用,如下所示:
1 <p>Reversed message: "{{ reversedMsg() }}"</p>
差異:不同的是計算屬性是基於它們的響應式依賴進行緩存的。 只在相關響應式依賴發生改變時它們才會重新求值。 這就意味著只要 msg 還沒有發生改變,多次訪問 reversedMsg計算屬性會立即返回之前的計算結果,而不必再次執行函數。 即:計算屬性只有當依賴屬性發生改變時,才重新計算,而函數需要每次都重新計算。
也同樣意味著下麵的計算屬性將不再更新,因為 Date.now() 不是響應式依賴: 相比之下,每當觸發重新渲染時,調用方法將總會再次執行函數。
1 <script type="text/javascript"> 2 var vm = new Vue({ 3 el: '#app', 4 data: { 5 msg: 'welcome to vue world!!!' 6 7 }, 8 computed: { 9 now: function() { 10 return Date.now().toString(); 11 } 12 13 }); 14 </script>
如下所示,將不會隨著時間的改變而觸發。
1 <p>{{now}}</p>
如果你不希望有緩存,請用方法來替代。
計算屬性 vs 偵聽屬性
如下所示:有兩個data屬性,firstName和lastName,fullName依賴說前兩個變化而變化。如果需要採用偵聽屬性,需要對firstName和lastName進行偵聽。
1 <script type="text/javascript"> 2 var vm = new Vue({ 3 el: '#app', 4 data: { 5 firstName: 'Foo', 6 lastName: 'Bar', 7 fullName: 'Foo Bar', 8 }, 10 computed: { 11 fullName2: function() { 12 return this.firstName + ' ' + this.lastName; 13 } 15 }, 16 watch: { 17 firstName: function(val) { 18 this.fullName = val + ' ' + this.lastName; 19 }, 20 lastName: function(val) { 21 this.fullName = this.firstName + ' ' + val; 22 } 24 } 26 }); 27 </script>
上面代碼是命令式且重覆的。將它與計算屬性的版本進行比較。
1 <div id="demo">{{ fullName }}</div> 2 <div id="demo">{{ fullName2 }}</div>
如上所示:fullName採用偵聽屬性,fullName2採用計算屬性,對比一下,好得多了,不是嗎?
計算屬性的 setter
計算屬性預設只有 getter ,不過在需要時你也可以提供一個 setter :如下所示:
1 fullName3: { 2 //getter 3 get: function() { 4 return this.firstName + ' ' + this.lastName; 5 }, 6 //setter 7 set: function(newValue) { 8 console.log('newValue=' + newValue); 9 var names = newValue.split(' '); 10 this.firstName = names[0]; 11 this.lastName = names[names.length - 1]; 12 } 13 }
在UI中引用的方式是一樣的。當fullName3的值發生改變時,將更新firstName和lastName。
1 <p>{{fullName3}}</p>
偵聽器
雖然計算屬性在大多數情況下更合適,但有時也需要一個自定義的偵聽器。這就是為什麼 Vue 通過 watch 選項提供了一個更通用的方法,來響應數據的變化。當需要在數據變化時執行非同步或開銷較大的操作時,這個方式是最有用的。如下所示:
1 <script type="text/javascript"> 2 var vm = new Vue({ 3 el: '#app', 4 data: { 5 question: '', 6 answer: 'I cannot give you an answer until you ask a question!' 7 8 }, 9 watch: { 10 // 如果 `question` 發生改變,這個函數就會運行 11 question: function(newQuestion, oldQuestion) { 12 if (newQuestion == '') { 13 this.answer = 'Waiting for you to stop typing...'; 14 } else { 15 this.answer = '請回答'; 16 } 17 } 18 } 19 }); 20 </script>
在頁面中綁定屬性
1 <p>Ask a yes/no question: 2 <input v-model="question"> 3 </p> 4 <p>{{ answer }}</p>
在這個示例中,使用 watch 選項允許我們執行非同步操作 (訪問一個 API),限制我們執行該操作的頻率,併在我們得到最終結果前,設置中間狀態。這些都是計算屬性無法做到的。
在本示例中用的全部代碼 ,如下所示:
1 <!DOCTYPE html> 2 <html> 3 <head> 4 <meta charset="utf-8"> 5 <title>一起學Vue之計算屬性和偵聽器</title> 6 <!-- 開發環境版本,包含了有幫助的命令行警告 --> 7 <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script> 8 </head> 9 <body> 10 <div id="app"> 11 <span>{{msg}}</span> 12 <h2>計算屬性</h2> 13 <!-- 14 模板內的表達式非常便利,但是設計它們的初衷是用於簡單運算的。在模板中放入太多的邏輯會讓模板過重且難以維護。例如: 15 --> 16 <br /> 17 <span> 18 {{ msg.split('').reverse().join('') }} 19 </span> 20 <!-- 21 在這個地方,模板不再是簡單的聲明式邏輯。你必須看一段時間才能意識到,這裡是想要顯示變數 msg 的翻轉字元串。 22 當你想要在模板中多次引用此處的翻轉字元串時,就會更加難以處理。 23 所以,對於任何複雜邏輯,你都應當使用計算屬性 24 --> 25 <p>原始信息: {{ msg }}</p> 26 <p>計算屬性反轉信息: {{ reverseMsg }}</p> 27 <!-- 28 這裡我們聲明瞭一個計算屬性 reverseMsg。我們提供的函數將用作屬性 vm.reverseMsg 的 getter 函數: 29 --> 30 <!-- 31 你可以像綁定普通屬性一樣在模板中綁定計算屬性。Vue 知道 vm.reverseMsg 依賴於 vm.msg, 32 因此當 vm.msg 發生改變時,所有依賴 vm.reverseMsg 的綁定也會更新。 33 --> 34 <h2>計算屬性緩存 vs 方法</h2> 35 <p>Reversed message: "{{ reversedMsg() }}"</p> 36 <!-- 37 你可能已經註意到我們可以通過在表達式中調用方法來達到同樣的效果: 38 --> 39 <!-- 40 我們可以將同一函數定義為一個方法而不是一個計算屬性。兩種方式的最終結果確實是完全相同的。 41 然而,不同的是計算屬性是基於它們的響應式依賴進行緩存的。 42 只在相關響應式依賴發生改變時它們才會重新求值。 43 這就意味著只要 msg 還沒有發生改變,多次訪問 reversedMsg 計算屬性會立即返回之前的計算結果,而不必再次執行函數。 44 即:計算屬性只有當依賴屬性發生改變時,才重新計算,而函數需要每次都重新計算。 45 --> 46 <!-- 47 也同樣意味著下麵的計算屬性將不再更新,因為 Date.now() 不是響應式依賴: 48 相比之下,每當觸發重新渲染時,調用方法將總會再次執行函數。 49 如果你不希望有緩存,請用方法來替代 50 --> 51 <p>{{now}}</p> 52 <h2>計算屬性 vs 偵聽屬性</h2> 53 <div id="demo">{{ fullName }}</div> 54 <!-- 55 上面代碼是命令式且重覆的。將它與計算屬性的版本進行比較: 56 --> 57 <div id="demo">{{ fullName2 }}</div> 58 <!-- 59 好得多了,不是嗎? 60 --> 61 <h2>計算屬性的 setter</h2> 62 <!-- 63 計算屬性預設只有 getter ,不過在需要時你也可以提供一個 setter : 64 --> 65 <p>{{fullName3}}</p> 66 <h2>偵聽器</h2> 67 <!-- 68 雖然計算屬性在大多數情況下更合適,但有時也需要一個自定義的偵聽器。 69 這就是為什麼 Vue 通過 watch 選項提供了一個更通用的方法,來響應數據的變化。 70 當需要在數據變化時執行非同步或開銷較大的操作時,這個方式是最有用的。 71 --> 72 <p>Ask a yes/no question: 73 <input v-model="question"> 74 </p> 75 <p>{{ answer }}</p> 76 <!-- 77 在這個示例中,使用 watch 選項允許我們執行非同步操作 (訪問一個 API), 78 限制我們執行該操作的頻率,併在我們得到最終結果前,設置中間狀態。 79 這些都是計算屬性無法做到的。 80 --> 81 </div> 82 <script type="text/javascript"> 83 var vm = new Vue({ 84 el: '#app', 85 data: { 86 msg: 'welcome to vue world!!!', 87 firstName: 'Foo', 88 lastName: 'Bar', 89 fullName: 'Foo Bar', 90 question: '', 91 answer: 'I cannot give you an answer until you ask a question!' 92 93 94 }, 95 methods: { 96 reversedMsg: function() { 97 return this.msg.split('').reverse().join('') 98 } 99 }, 100 computed: { 101 reverseMsg: function() { 102 // `this` 指向 vm 實例 103 return this.msg.split('').reverse().join(''); 104 }, 105 now: function() { 106 return Date.now().toString(); 107 }, 108 fullName2: function() { 109 return this.firstName + ' ' + this.lastName; 110 }, 111 fullName3: { 112 //getter 113 get: function() { 114 return this.firstName + ' ' + this.lastName; 115 }, 116 //setter 117 set: function(newValue) { 118 console.log('newValue=' + newValue); 119 var names = newValue.split(' '); 120 this.firstName = names[0]; 121 this.lastName = names[names.length - 1]; 122 } 123 } 124 }, 125 watch: { 126 firstName: function(val) { 127 this.fullName = val + ' ' + this.lastName; 128 }, 129 lastName: function(val) { 130 this.fullName = this.firstName + ' ' + val; 131 }, 132 // 如果 `question` 發生改變,這個函數就會運行 133 question: function(newQuestion, oldQuestion) { 134 if (newQuestion == '') { 135 this.answer = 'Waiting for you to stop typing...'; 136 } else { 137 this.answer = '請回答'; 138 } 139 } 140 141 } 142 143 }); 144 </script> 145 </body> 146 </html>View Code
備註
暴虎馮河,死而無悔者,吾不與也;必也,臨事而懼,好謀而成者也。