這一節咱們一塊兒學習 vue
中的計算屬性(computed properties
)和偵聽器(watch
)。javascript
在以前,咱們學習過 vue
表達式插值:html
<div id="example"> {{ message.split('').reverse().join('') }} </div>
若是在模板中放入太多的邏輯會讓模板太重且難以維護。咱們能夠把方法寫在事件處理函數裏面,而且在構造器內部經過 this
調用。
<!-- more -->Html
代碼:vue
<h1>{{ this.reversedMessageMethod() }}</h1>
JS
代碼:java
methods: { reversedMessageMethod() { return this.message.split('').reverse().join(''); }, },
這裏要提醒一下,咱們不能夠把事件處理函數寫成箭頭函數,由於這裏的this
須要指向vue
實例。
其實,模板中的處理邏輯是不適合放在事件處理函數裏的,即便能夠這麼作。事件處理函數應該專一於處理事件,咱們應該讓它變得純粹。在 vue
中, 容許咱們自定義過濾器(filters
),可被用於一些常見的文本格式化。git
過濾器能夠用在兩個地方:雙花括號插值和 v-bind
表達式 (後者從 2.1.0+
開始支持)。過濾器應該被添加在 JavaScript
表達式的尾部,由 |
符號指示,咱們看看怎麼用:github
<template> <div class='hello'> <h1>{{ message.split('').reverse().join('') }}</h1> <!-- add this --> <h1>{{ message | reverseString }}</h1> </div> </template> <script> export default { name: 'HelloWorld', data() { return { message: 'Welcome to Your Vue.js App', }; }, // add this filters: { reverseString(val) { let value = val; if (!value) return ''; value = value.split('').reverse().join(''); return value; }, }, }; </script>
代碼增多了,可是總體的語義化卻更好。從 filters
的用法咱們能夠看出:web
因此,filters
的缺點就很明顯了:若是要計算結合多個數據不一樣變化的狀況,過濾器就沒法適用了。這就要用到咱們下面提到的計算屬性了。ajax
在 vue
中,也爲咱們提供了computed
這個選項來處理數據,咱們稱它爲計算屬性。當邏輯複雜的時候,咱們就應當使用 computed
計算屬性了。計算屬性使用起來很是簡單,咱們仍是用上面的例子,使用計算屬性來修改模板中的邏輯:數組
<template> <div class='hello'> <h1>{{ message.split('').reverse().join('') }}</h1> <h1>{{ message | reverseString }}</h1> <h1>{{ this.reversedMessageMethod() }}</h1> <!-- add this --> <h1>{{ reversedMessage }}</h1> </div> </template> <script> export default { name: 'HelloWorld', data() { return { message: 'Welcome to Your Vue.js App', }; }, filters: { reverseString(val) { let value = val; if (!value) return ''; value = value.split('').reverse().join(''); return value; }, }, // add this computed: { reversedMessage() { return this.message.split('').reverse().join(''); }, }, methods: { reversedMessageMethod() { return this.message.split('').reverse().join(''); }, }, }; </script>
和 method
選項的使用很是類似。咱們能夠像綁定普通屬性同樣在模板中綁定計算屬性。緩存
這兩種方式的最終結果確實是徹底相同的。然而,不一樣的是:計算屬性是基於它們的依賴進行緩存的。什麼意思呢?
只在相關依賴發生改變時它們纔會從新求值。這就意味着只要 message
尚未發生改變,屢次訪問 reversedMessage
計算屬性會當即返回以前的計算結果,而沒必要再次執行函數;而當數據有變化時,只要有一個數據發生變化,則會從新計算,來更新視圖的改變。相比之下,每當觸發從新渲染時,調用 methods
中的方法將總會再次執行函數。
咱們爲何須要緩存?假設咱們有一個性能開銷比較大的計算屬性
A,它須要遍歷一個巨大的數組並作大量的計算。而後咱們可能有其餘的計算屬性依賴於
A 。若是沒有緩存,咱們將不可避免的屢次執行
A 的
getter
!
咱們來看看計算屬性的具體應用場景:
一、微博發文
發微博的時候,有字數限制。在咱們輸入文字的時候,輸入框會計算咱們還能夠輸入多少字:
<template> <div> <textarea v-model='content' :maxlength='totalcount'></textarea> <p>你還能夠輸入{{reduceCount}}字</p> </div> </template> <script> export default { data() { return { totalcount: 200, // 總共只給輸入200字 content: '', }; }, computed: { reduceCount() { return this.totalcount - this.content.length; }, }, }; </script>
經過一直監聽輸入的字符的長度來觸發 computed
裏的 reduceCount
方法,從新計算,而後返回給視圖,讓視圖做出相應的變化。接下來咱們再看一個例子。
二、足球比賽
這個例子是一個足球比賽的結果播報板,咱們先看看最終的效果,再看代碼:
效果 :
代碼:
<template> <div> <h1>比賽時間:{{time}}s</h1> <h2>直播播報:{{result}}</h2> <div class='team'> <div> <p>中國隊進球數:{{team.china}}</p> <button @click='team.china++'>點擊中國隊進一球</button> </div> <div> <p>韓國隊進球數:{{team.korea}}</p> <button @click='team.korea++'>點擊韓國隊進一球</button> </div> </div> </div> </template> <script> export default { created() { const time = setInterval(() => { this.time = this.time + 1; if (this.time === 90) { clearInterval(time); } }, 1000); }, data() { return { time: 0, team: { china: 0, korea: 0, }, }; }, computed: { result() { if (this.time < 90) { if (this.team.china > this.team.korea) { return '中國隊領先'; } else if (this.team.china < this.team.korea) { return '韓國隊領先'; } return '雙方僵持'; } if (this.team.china > this.team.korea) { return '中國隊贏'; } else if (this.team.china < this.team.korea) { return '韓國隊贏'; } return '平局'; }, }, }; </script> <style scoped> .team { display: flex; justify-content: center; } button { padding: 15px 60px; outline: none; background-color: #27ae60; display: block; font-size: 1rem; color: #fff; margin: 10px; } </style>
經過上面的例子,咱們就能夠很清楚 computed
的做用了:觀察一個或者多個數據,只要依賴的數據發生變化的時,這個函數就會從新計算。這樣咱們就能夠經過觀察全部數據來維護一個狀態,也就是所謂的返回一個狀態值。
雖然計算屬性在大多數狀況下更合適,但有時也須要一個自定義的偵聽器。vue
經過 watch
選項提供了一個更通用的方法,來響應數據的變化。
computed
和 watch
均可以作同一件事,兩個選項都是對數據進行時時監聽。可是它們也有不一樣:
computed
對多數據變更進行監聽,返回一個狀態,維護一個狀態watch
是對一個數據監聽,在數據變化時,會返回兩個值,一個是 value
(當前值),二是 oldvalue
是變化前的值咱們也能夠用 watch
選項來改造上面的足球比賽的例子,添加一個 watch
選項:
<script> export default { created() { const time = setInterval(() => { this.time = this.time + 1; if (this.time === 90) { clearInterval(time); } }, 1000); }, data() { return { time: 0, team: { china: 0, korea: 0, }, }; }, // add this watch: { time(value, oldval) { // eslint-disable-line if (value < 90) { if (this.team.china > this.team.korea) { this.result = '中國隊領先'; } else if (this.team.china < this.team.korea) { this.result = '韓國隊領先'; } else { this.result = '雙方僵持'; } } else if (this.team.china > this.team.korea) { this.result = '中國隊贏'; } else if (this.team.china < this.team.korea) { this.result = '韓國隊贏'; } else { this.result = '平局'; } }, team(value, oldval) { // eslint-disable-line if (this.time < 90) { if (value.china > value.korea) { this.result = '中國隊領先'; } else if (value.china < value.korea) { this.result = '韓國隊領先'; } else { this.result = '雙方僵持'; } } else if (value.china > value.korea) { this.result = '中國隊贏'; } else if (value.china < value.korea) { this.result = '韓國隊贏'; } else { this.result = '平局'; } }, }, }; </script>
能夠看出跟使用 computed
選項是差很少一致的。那 watch
有什麼應用場景呢?
有一個很常見的場景:圖片的預加載。當圖片數量比較大的時候,爲了保證頁面圖片都加載出來的時候,才把主頁面給顯示出來,而後再進行一些 ajax
請求,或者邏輯操做,這個時候用 computed
就沒法實現了,只能用 watch
,看看代碼:
<template> <div v-show='show'> <img src='https://img.alicdn.com/simba/img/TB14sYVQXXXXXc1XXXXSutbFXXX.jpg' alt> <img src='//img.alicdn.com/tfs/TB1iZ6EQXXXXXcsXFXXXXXXXXXX-520-280.jpg_q90_.webp' alt> <img src='https://img.alicdn.com/simba/img/TB1C0dOPXXXXXarapXXSutbFXXX.jpg' alt> <img src='//img.alicdn.com/tfs/TB1iZ6EQXXXXXcsXFXXXXXXXXXX-520-280.jpg_q90_.webp' alt> </div> </template> <script> export default { data() { return { count: 0, show: false, }; }, mounted() { const imgs = document.querySelectorAll('img'); console.log(imgs); // eslint-disable-line Array.from(imgs).forEach((item) => { const img = new Image(); img.onload = () => { this.count = this.count + 1; }; img.src = item.getAttribute('src'); }); }, watch: { count(val, oldval) { // eslint-disable-line if (val === 4) { this.show = true; alert('加載完畢'); // eslint-disable-line // 而後能夠對後臺發送一些ajax操做 } }, }, }; </script>
咱們能夠發現等四張圖片都加載完畢的時候頁面才顯示出來。因此當咱們想要在數據變化響應時,執行異步操做或開銷較大的操做,就須要使用 watch
了。
過濾器 filter
:
計算屬性 computed
:
偵聽器 watch
:
value
(當前值),二是 oldvalue
是變化前的值
注:本節內容的
demo
均來自
混元霹靂手,感謝做者!
歡迎關注個人博客: https://togoblog.cn/