理解Vue的計算屬性


  計算屬性是一個很邪門的東西,只要在它的函數里引用了 data 中的某個屬性,當這個屬性發生變化時,函數仿佛可以嗅探到這個變化,並自動重新執行。

  上述代碼會源源不斷的打印出 b 的值。如果希望 a 依賴 data 中的 x 而變化,只需保證 a 函數中有 this.x 即可。如果函數中沒有出現 data 中的屬性,那么無論 data 中的屬性怎么變,a 對應的函數一次也不會執行。

      Vue 怎么知道計算屬性在函數中引用了哪個 data 屬性?這個函數又是怎么知道 data 屬性變了,而且只關心它內部引用的那個屬性,別的都不管?

  官方文檔對計算屬性的描述是:

  模板內的表達式非常便利,但是設計它們的初衷是用於簡單運算的。在模板中放入太多的邏輯會讓模板過重且難以維護。所以,對於任何復雜邏輯,你都應當使用計算屬性。

  你可以像綁定普通屬性一樣在模板中綁定計算屬性。Vue 知道 vm.reversedMessage 依賴於 vm.message,因此當 vm.message 發生改變時,所有依賴 vm.reversedMessage 的綁定也會更新。而且最妙的是我們已經以聲明的方式創建了這種依賴關系:計算屬性的 getter 函數是沒有副作用 (side effect) 的,這使它更易於測試和理解。

1、computed VS methods

  你可能已經注意到我們可以通過在表達式中調用方法來達到同樣的效果,我們可以將同一函數定義為一個方法而不是一個計算屬性。兩種方式的最終結果確實是完全相同的。然而,不同的是計算屬性是基於它們的依賴進行緩存的計算屬性只有在它的相關依賴發生改變時才會重新求值。這就意味着只要 message 還沒有發生改變,多次訪問 reversedMessage 計算屬性會立即返回之前的計算結果,而不必再次執行函數。(緩存的效果)這也同樣意味着下面的計算屬性將不再更新,因為 Date.now() 不是響應式依賴:

computed: { now: function () { return Date.now() } }

  相比之下,每當觸發重新渲染時,調用方法將總會再次執行函數。

  我們為什么需要緩存?假設我們有一個性能開銷比較大的的計算屬性 A,它需要遍歷一個巨大的數組並做大量的計算。然后我們可能有其他的計算屬性依賴於 A 。如果沒有緩存,我們將不可避免的多次執行 A 的 getter!如果你不希望有緩存,請用方法來替代。

2、計算屬性緩存

  在 0.12.8 之前,計算屬性僅僅體現為一個取值的行為 —— 每次你訪問它的時候,getter 都會重新求值。在 0.12.8 中對此做了改進 —— 計算屬性的值會被緩存,只有在其某個反應依賴改變才會重新計算。設想我們有一個開銷很大的計算屬性 A,它需要循環一個大數組並且完成很多運算。並且我們還有一個依賴 A 的計算屬性。如果沒有緩存,我們對 A 的 getter 不必要的過度調用就會導致潛在的性能問題。而有了緩存,A 的值會被緩存起來,除非其依賴發生了變化,這樣訪問它再多次也不會導致大量的不必要運算了。

  然而,我們還是應該理解什么會被視為“反應式依賴”:

var vm = new Vue({ data: { msg: 'hi' }, computed: { example: { return Date.now() + this.msg } } })

  在上面的例子中,計算屬性依賴 vm.msg。因為這是一個在 Vue 實例中被觀察的數據屬性,那么它就被視為了一個反應式依賴。無論何時 vm.msg 改變,vm.example 的值都會被重新計算。然而,Date.now()並不是反應式依賴,因為它沒有和 Vue 的數據觀察系統發生任何關系。因此,當你在程序中訪問vm.example時,你會發現除非vm.msg觸發了一次重新計算,否則時間戳始終是相同的值

  有的時候你需要保留簡單獲取數據的模式,每次你訪問 vm.example 的時候都希望觸發重新計算。從 0.12.11 開始,你可以為一個特殊的計算屬性開關緩存支持:

computed: { example: { cache: false, get: function () { return Date.now() + this.msg } } }

  現在,每次你訪問 vm.example 的時候,時間戳都會及時更新。然而,要注意這只發生在 JavaScript 程序內部訪問的時候,數據綁定還是依賴驅動的。當你在模板中綁定一個 {{example}} 的計算屬性時,這點需要注意:DOM 只會在反應式依賴改變時才會更新

3、computed VS watch

  Vue 提供了一種更通用的方式來觀察和響應 Vue 實例上的數據變動:偵聽屬性。當你有一些數據需要隨着其它數據變動而變動時,你很容易濫用 watch——特別是如果你之前使用過 AngularJS。然而,通常更好的做法是使用計算屬性而不是命令式的 watch 回調。細想一下這個例子:

<div id="demo">{{ fullName }}</div>

var vm = new Vue({ el: '#demo', data: { firstName: 'Foo', lastName: 'Bar', fullName: 'Foo Bar' }, watch: { firstName: function (val) { this.fullName = val + ' ' + this.lastName }, lastName: function (val) { this.fullName = this.firstName + ' ' + val } } })

  上面代碼是命令式且重復的。將它與計算屬性的版本進行比較,會發現計算屬性好多了。

var vm = new Vue({ el: '#demo', data: { firstName: 'Foo', lastName: 'Bar' }, computed: { fullName: function () { return this.firstName + ' ' + this.lastName } } })

  雖然計算屬性在大多數情況下更合適,但有時也需要一個自定義的偵聽器。這就是為什么 Vue 通過 watch 選項提供了一個更通用的方法,來響應數據的變化。當需要在數據變化時執行異步或開銷較大的操作時,watch這個方式是最有用的。

4、計算屬性默認只有 getter ,不過在需要時你也可以提供一個 setter :

computed: { fullName: { // getter
    get: function () { return this.firstName + ' ' + this.lastName }, // setter
    set: function (newValue) { var names = newValue.split(' ') this.firstName = names[0] this.lastName = names[names.length - 1] } } }

  現在再運行 vm.fullName = 'John Doe' 時,setter 會被調用,vm.firstNamevm.lastName 也會相應地被更新。

 5、計算屬性禁用箭頭函數

  注意,不應該使用箭頭函數來定義計算屬性函數 (例如 aDouble: () => this.a * 2)。理由是箭頭函數綁定了父級作用域的上下文,所以 this 將不會按照期望指向 Vue 實例,this.a 將是 undefined

  當然有很多this需要指向vue實例的時候,都需要慎用箭頭函數

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM