{ [key: string]: Function | { get: Function, set: Function } }
計算屬性的結果會被緩存,除非依賴的響應式屬性變化纔會從新計算。注意,若是某個依賴 (好比非響應式屬性) 在該實例範疇以外,則計算屬性是不會被更新的。javascript
上面這幾段話其實能夠概括爲如下幾點:前端
computed
是計算屬性,會被混入到Vue
實例中computed
的結果會被緩存,除非依賴的響應式屬性變化纔會從新計算 computed
?同以往同樣,先新建一個Vue
項目,同時加入如下代碼:java
export default { name: 'test', data () { return { app: 666 } }, created () { console.log('app proxy -->', this.appProxy) }, computed () { appProxy () { debugger return this.app } } }
F12
打開調試界面,刷新後斷點停在了debugger
的位置,同時能夠看到右邊的調用棧:express
appProxy
get
evaluate
computedGetter
created
瞥到computedGetter
以後,點進去,能夠看到:緩存
function createComputedGetter (key) { return function computedGetter () { var watcher = this._computedWatchers && this._computedWatchers[key]; if (watcher) { if (watcher.dirty) { watcher.evaluate(); } if (Dep.target) { watcher.depend(); } return watcher.value } } }
看到這裏不由一臉懵逼😬
固然,根據前面咱們看源碼的經驗,沒有思路時,直接搜索相關函數的調用位置,這裏咱們能夠直接搜索createComputedGetter
,看看它是在哪裏調用的。此處忽略搜索的過程,直接給出個人結論:微信
Vue
中存在兩種初始化computed
的方法:app
option
中初始化Vue.prototype.extend
函數中初始化這兩種初始化其實大同小異,咱們選擇在組件中寫computed
,天然斷點就會跑到Vue.prototype.extend
函數裏:函數
... if (Sub.options.computed) { initComputed$1(Sub); } ...
initComputed$1
函數:oop
function initComputed$1 (Comp) { // 拿到組件的computed var computed = Comp.options.computed; for (var key in computed) { // 循環遍歷 defineComputed(Comp.prototype, key, computed[key]); } }
顯然,這句代碼:defineComputed(Comp.prototype, key, computed[key])
將computed
掛載在了組件的原型上,下面來看下它的實現方式:this
defineComputed
:
function defineComputed ( target, key, userDef ) { // 判斷是否要將結果緩存下來 var shouldCache = !isServerRendering(); // 下面進行分類判斷 // 對應的computed是函數的狀況 if (typeof userDef === 'function') { sharedPropertyDefinition.get = shouldCache ? createComputedGetter(key) : createGetterInvoker(userDef); sharedPropertyDefinition.set = noop; } else { // 非函數的狀況 sharedPropertyDefinition.get = userDef.get ? shouldCache && userDef.cache !== false ? createComputedGetter(key) : createGetterInvoker(userDef.get) : noop; sharedPropertyDefinition.set = userDef.set || noop; } if (process.env.NODE_ENV !== 'production' && sharedPropertyDefinition.set === noop) { sharedPropertyDefinition.set = function () { warn( ("Computed property \"" + key + "\" was assigned to but it has no setter."), this ); }; } // 將sharedPropertyDefinition綁定到組件對象上 Object.defineProperty(target, key, sharedPropertyDefinition); }
😅感受有點亂,最後再梳理下上邊的邏輯:
initComputed
:
initComputed
,從Vue
中拿到computed
對象裏全部的key
值key
值,調用defineComputed
函數,把computed
綁定到組件對象上defineComputed
:
computed
的結果會被緩存,不是則不會緩存計算結果computed
存在兩種寫法,這裏也對函數跟對象的寫法作了區分computed
的結果緩存是如何實現的?
上面咱們大體梳理了下
computed
的初始化邏輯,如今咱們回過頭來再看一下官方定義,發現其中提到了
計算屬性會將計算結果緩存下來,那麼這個計算結果究竟是怎麼被緩存下來的呢?
defineComputed
defineComputed
裏最後將sharedPropertyDefinition
綁定到組件對象上,在代碼裏面能夠看到對sharedPropertyDefinition.get
作了特殊處理,兩種狀況分別封裝了:
createComputedGetter
createGetterInvoker
createComputedGetter
的實現:
function createComputedGetter (key) { return function computedGetter () { var watcher = this._computedWatchers && this._computedWatchers[key]; if (watcher) { if (watcher.dirty) { watcher.evaluate(); } if (Dep.target) { watcher.depend(); } return watcher.value } } }
createGetterInvoker
的實現:
function createGetterInvoker(fn) { return function computedGetter () { return fn.call(this, this) } }
能夠看到,服務端渲染確實是對計算屬性的結果不作緩存的,可是咱們對結果是如何緩存,依舊是一臉懵逼😐
刷新頁面回到一開始咱們在appProxy
中打下的斷點,在調用棧中有兩個顯眼的函數:
evaluate
get
分別點進去,咱們能夠看到:
evaluate
實現源碼:
Watcher.prototype.evaluate = function evaluate () { this.value = this.get(); this.dirty = false; };
get
實現源碼:
Watcher.prototype.get = function get () { pushTarget(this); var value; var vm = this.vm; try { value = this.getter.call(vm, vm); } catch (e) { if (this.user) { handleError(e, vm, ("getter for watcher \"" + (this.expression) + "\"")); } else { throw e } } finally { // "touch" every property so they are all tracked as // dependencies for deep watching if (this.deep) { traverse(value); } popTarget(); this.cleanupDeps(); } return value };
結合上面給出的createComputedGetter
源碼咱們能夠知道,computed
的計算結果是經過Watcher.prototype.get
來獲得的,拿到value
之後,在Wathcer.prototype.evaluate
中執行了這樣一行代碼:
... this.dirty = false;
聰明的讀者確定猜到了,計算屬性是否從新計算結果,確定跟這個屬性有關。接下來咱們只要跟蹤這個屬性的變化,就能夠輕鬆的知道計算屬性的緩存原理了。
掃描下方的二維碼或搜索「tony老師的前端補習班」關注個人微信公衆號,那麼就能夠第一時間收到個人最新文章。