computed 只會在值用到的時候纔會執行函數函數
下文會以此做爲依據進行講解ui
class Foo{
@computed get age() { return expr; } // 推薦用法
}
複製代碼
經過調用 createPropDecorator
方法給原型對象建立一個不可枚舉的 __mobxDecorators
屬性對象,age
會做爲屬性對象的 key
存在,此方法會返回一個描述符。this
// 添加 __mobxDecorators 屬性
const inheritedDecorators = target.__mobxDecorators
addHiddenProp(target, "__mobxDecorators", { ...inheritedDecorators })
// 描述符
{
configurable: true,
enumerable: enumerable,
get() {
initializeInstance(this)
return this[prop]
},
set(value) {
initializeInstance(this)
this[prop] = value
}
}
複製代碼
咱們發現這裏劫持了屬性的 get
和 set
操做,每當執行 get
和 set
操做以前,都會先執行 initializeInstance
。spa
判斷實例上 __mobxDidRunLazyInitializers
是否爲 true
,若是是則表明對象已經處於可監聽狀態。不然取原型上的 __mobxDecorators
對象,並遍歷此對象的每個 propertyCreator
方法,也就是以前 createPropDecorator
方法的第二個參數。propertyCreator
方法內部把描述符上 get
和 set
與 decoratorArgs[0]
當作 defineComputedProperty
第三個參數傳遞進去。code
const { get, set } = descriptor
const options = decoratorArgs[0] || {}
defineComputedProperty(instance, propertyName, { get, set, ...options })
複製代碼
const adm = asObservableObject(target)
options.name = `${adm.name}.${propName}`
options.context = target
adm.values[propName] = new ComputedValue(options)
Object.defineProperty(target, propName, generateComputedPropConfig(propName))
複製代碼
asObservableObject
主要做用爲在實例上新增不可枚舉的 $mobx
屬性,age
會做爲屬性的 key
存在,屬性值爲 ObservableObjectAdministration
實例。server
修改 adm.values[propName]
爲 ComputedValue
的實例,實例上新增不可枚舉的 age
屬性,並劫持 get
和 set
,每當訪問或設置 age
屬性時,其實是訪問 $mobx
原型上的 read
和 write
。對象
function generateComputedPropConfig(propName){
return {
get() {
return getAdministrationForComputedPropOwner(this).read(this, propName)
},
set(v) {
getAdministrationForComputedPropOwner(this).write(this, propName, v)
}
}
}
function getAdministrationForComputedPropOwner(owner) {
const adm = owner.$mobx
if (!adm) {
initializeInstance(owner)
return owner.$mobx
}
return adm
}
複製代碼
前文說過,訪問 age
其實是訪問 $mobx
上的 read
,而 read
又返回 values.age
值,也就是至關於訪問 ComputedValue
實例的 get
方法。總結下,訪問 age
至關於訪問 ComputedValue
實例的 get
方法。隊列
若是當前不處於事務處理,又沒有觀察者且 keepAlive
爲 false
狀態,則開啓事務處理,並執行 age
函數,並把返回值保存在 value
中,並結束事務處理。事務
if (globalState.inBatch === 0 && this.observers.length === 0 && !this.keepAlive) {
// NOT_TRACKING
if (shouldCompute(this)) {
this.warnAboutUntrackedRead();
startBatch(); // See perf test 'computed memoization'
this.value = this.computeValue(false);
endBatch();
}
}
複製代碼
若是當前處於事務處理或有觀察者或 keepAlive
爲 true
,則調用 reportObserved
,在正在跟蹤的 derivation
中加入當前實例,並設置實例已處於監聽狀態;若是當前實例沒有觀察者且當前處於事務中,則把當前實例放入到隊列中,等待移除監聽狀態。執行 age
函數,並刷新監聽對象依賴關係,把 age
函數執行結果返回出去,並用此值與舊值進行比對,若是不一致,則使用新值,並改變內部狀態。ip
function computeValue(track){
...
res = trackDerivedFunction(this, this.derivation, this.scope)
...
}
複製代碼