計算屬性是一個很邪門的東西,只要在它的函數里引用了 data 中的某個屬性,當這個屬性發生變化時,函數仿佛可以嗅探到這個變化,並自動重新執行。
上述代碼會源源不斷的打印出 b 的值。如果希望 a 依賴 data 中的 x 而變化,只需保證 a 函數中有 this.x 即可。如果函數中沒有出現 data 中的屬性,那么無論 data 中的屬性怎么變,a 對應的函數一次也不會執行。
Vue 怎么知道計算屬性在函數中引用了哪個 data 屬性?這個函數又是怎么知道 data 屬性變了,而且只關心它內部引用的那個屬性,別的都不管?
官方文檔對計算屬性的描述是:
文檔的描述讓我的困惑更加困惑,還有這種操作?這特么是怎么做到的?
Google 了一把,看了一篇三哥的博文(見文末),豁然開朗。
我們簡單模擬實現一個計算屬性:a 變化時,b 自動跟着變化。
由於涉及 Vue 的響應式綁定的原理,如果你對此不熟,最好先看看《Vue.js 雙向綁定的實現原理》
少啰嗦,先看過程:
1. 首先 b 屬性會被處理為存取器屬性,訪問 b 就會觸發其 get 函數
2. 處理計算屬性 a 時,會執行 a 的函數,從而會執行 this.b,於是觸發 b 的 get 函數
3. b 的 get 函數會添加 b 屬性的依賴項,而剛才在處理計算屬性過程中,a 已經作為依賴項被傳給了一個全局變量,b 的 get 函數會檢測到這個全局變量,並將其添加到自身的訂閱者列表中
4. 對 b 賦予新的值時,會觸發其 set 函數,set 函數中會遍歷執行訂閱者,a 的值就是在這個時候更新的
再看代碼:
(注:圖中數字僅作思路引導,並非與前文過程描述對應)
測試一下,完美打印出 1, 2, 3, 4
console.log(obj.b)
obj.a += 1;
console.log(obj.b);
obj.a += 1;
console.log(obj.b);
obj.a += 1;
console.log(obj.b);
通過對存取器屬性、閉包和觀察者模式的綜合運用,Vue 巧妙的實現了計算屬性。現在再看官方文檔描述,是不是更通透了呢。
可以看出,Vue 響應式系統的核心理念是“依賴”,DOM 節點之所以隨數據而變化,是因為節點依賴於數據,計算屬性之所以隨數據而變化,是因為計算屬性依賴於數據。做好響應式的關鍵就在於處理好依賴關系。
參考文章:https://skyronic.com/blog/vuejs-internals-computed-properties