{ [key: string]: Function | { get: Function, set: Function } }
上面這幾段話其實能夠概括爲如下幾點:javascript
computed
是計算屬性,會被混入到Vue
實例中computed
的結果會被緩存,除非依賴的響應式屬性變化纔會從新計算computed
?同以往同樣,先新建一個Vue
項目,同時加入如下代碼:前端
export default {
name: 'test',
data () {
return {
app: 666
}
},
created () {
console.log('app proxy -->', this.appProxy)
},
computed () {
appProxy () {
debugger
return this.app
}
}
}
複製代碼
F12
打開調試界面,刷新後斷點停在了debugger
的位置,同時能夠看到右邊的調用棧:java
appProxy
get
evaluate
computedGetter
created
瞥到computedGetter
以後,點進去,能夠看到:express
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
的方法:微信
option
中初始化Vue.prototype.extend
函數中初始化這兩種初始化其實大同小異,咱們選擇在組件中寫computed
,天然斷點就會跑到Vue.prototype.extend
函數裏:app
...
if (Sub.options.computed) {
initComputed$1(Sub);
}
...
複製代碼
initComputed$1
函數:函數
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
掛載在了組件的原型上,下面來看下它的實現方式:oop
defineComputed
:ui
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老師的前端補習班」關注個人微信公衆號,那麼就能夠第一時間收到個人最新文章。