Mobx 源碼分析 - computed

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
    }
}
複製代碼

咱們發現這裏劫持了屬性的 getset 操做,每當執行 getset 操做以前,都會先執行 initializeInstancespa

initializeInstance

判斷實例上 __mobxDidRunLazyInitializers 是否爲 true,若是是則表明對象已經處於可監聽狀態。不然取原型上的 __mobxDecorators 對象,並遍歷此對象的每個 propertyCreator 方法,也就是以前 createPropDecorator 方法的第二個參數。propertyCreator 方法內部把描述符上 getsetdecoratorArgs[0] 當作 defineComputedProperty 第三個參數傳遞進去。code

const { get, set } = descriptor
const options = decoratorArgs[0] || {}
defineComputedProperty(instance, propertyName, { get, set, ...options })
複製代碼

defineComputedProperty

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 屬性,並劫持 getset,每當訪問或設置 age 屬性時,其實是訪問 $mobx 原型上的 readwrite對象

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
}
複製代碼

ObservableObjectAdministration

前文說過,訪問 age 其實是訪問 $mobx 上的 read,而 read 又返回 values.age 值,也就是至關於訪問 ComputedValue 實例的 get 方法。總結下,訪問 age 至關於訪問 ComputedValue 實例的 get 方法。隊列

ComputedValue

若是當前不處於事務處理,又沒有觀察者且 keepAlivefalse 狀態,則開啓事務處理,並執行 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();
    }
}
複製代碼

若是當前處於事務處理或有觀察者或 keepAlivetrue,則調用 reportObserved,在正在跟蹤的 derivation 中加入當前實例,並設置實例已處於監聽狀態;若是當前實例沒有觀察者且當前處於事務中,則把當前實例放入到隊列中,等待移除監聽狀態。執行 age 函數,並刷新監聽對象依賴關係,把 age 函數執行結果返回出去,並用此值與舊值進行比對,若是不一致,則使用新值,並改變內部狀態。ip

function computeValue(track){
    ...
    res = trackDerivedFunction(this, this.derivation, this.scope)
    ...
}
複製代碼
相關文章
相關標籤/搜索