Mobx 源碼初探 - observable(一)

observable 同時支持 decorator 方式和方法調用方式。

// 示例
@observable name = '張三';
const temp = observable.box(20);

在源碼中可以發現 mobxobservable 上綁定了很多方法。

Object.keys(observableFactories).forEach(function(name) {
    return (observable[name] = observableFactories[name]);
});
本篇文章重點介紹 @observable 調用,其餘的後續文章會繼續講解。

@observable 有一個很強大的功能,它會對於不同類型,應用不同的轉換規則。它究竟是如何處理的?

createObservable

當我們使用 @observable,實質上是調用了 createObservable 的返回的 deepDecorator 函數。

createObservable 接收 3 個參數 varg2arg3,這三個參數分別對應構造函數的原型對象、屬性名、描述符。

function createObservable(v, arg2, arg3) {
    // @observable someProp;
    if (typeof arguments[1] === 'string') {
        return deepDecorator.apply(null, arguments);
    }
    ...
}

createDecoratorForEnhancer

deepDecorator 函數是 createDecoratorForEnhancer 的返回值,它把 deepEnhancer 作爲參數傳遞進去。deepDecorator 的具體功能就是針對不同類型使用不同方式的 observable

var deepDecorator = createDecoratorForEnhancer(deepEnhancer);

createDecoratorForEnhancer 函數會返回一個 res,所以說當調用 @observable 時候就是等於執行 res 函數,傳遞進去的 deepEnhancer 會作爲 resenhancer 屬性存在。

createPropDecorator

createDecoratorForEnhancer 方法內部會通過 createPropDecorator 函數生成 rescreatePropDecorator 函數接收 2 個參數,第一個參數 propertyInitiallyEnumerable 設置爲 true ( 內部寫死 ),第二個參數 propertyCreator 爲傳遞進來的函數。

createPropDecorator 函數返回 decoratorFactory 函數。看到這裏,我們先整理下,當我們編寫 @observable,實質上就是在調用 decoratorFactory 函數。

decoratorFactory

decoratorFactory 函數內部定義 decorator 函數,當調用時,會先判斷當前的調用方式,如果是 @decorator 方式調用,則直接執行 decorator 函數,否則返回 decorator 函數。

decorator 函數內部會首先判斷構造函數的原型對象上是否存在 __mobxDecorators 屬性,如果不存在,則定義此屬性,並通過 addHiddenProp 方法設置描述符。

function addHiddenProp(object, propName, value) {
    Object.defineProperty(object, propName, {
        enumerable: false,
        writable: true,
        configurable: true,
        value: value // 此時爲空對象
    });
}

當構造函數原型對象上存在 __mobxDecorators 屬性,則執行下面代碼。

target.__mobxDecorators[prop] = {
    prop: prop, // 屬性名
    propertyCreator: propertyCreator, // 傳遞進來的函數
    descriptor: descriptor, // 描述符
    decoratorTarget: target, // 構造函數原型對象
    decoratorArguments: decoratorArguments // 此時爲 []
};

createPropertyInitializerDescriptor

最後通過調用 createPropertyInitializerDescriptor 函數爲屬性生成描述符。createPropertyInitializerDescriptor 函數內部會根據是否可枚舉進行分類,並以屬性名作爲緩存對象的 key,生成的描述符作爲 value 存在。

尤其需要注意的是,描述符中有 getset 方法,這兩個方法內部都會首先調用 initializeInstance 方法,然後才執行對應的數據操作。

initializeInstance

initializeInstance 方法會首先判斷原型對象是否 __mobxDidRunLazyInitializers 屬性,如果存在,則後續都不執行。如果不存在,則會依次調用原型對象上 __mobxDecorators 屬性對應的 propertyCreator 方法。

看到這裏,可能有人就懵了,問 propertyCreator 方法哪來的?還記得我們在調用 createPropDecorator 函數傳遞進去的第二個參數嗎?這個方法就是那來的。propertyCreator 內部首先會判斷屬性描述符中是否存在 get,這裏的屬性描述符是原有的屬性描述符,而不是封裝後的。如果存在 get 方法,則報錯,否則繼續執行。判斷描述符是否存在,不存在則設置初始值爲 undefined,存在則繼續判斷是否有 initializer 方法,如果沒有,則初始值爲描述符的 value。如果有此方法,否則執行此方法,獲取屬性初始值。

var initialValue = descriptor
    ? descriptor.initializer
        ? descriptor.initializer.call(target)
        : descriptor.value
    : undefined;

defineObservableProperty

初始值獲取之後,調用 defineObservableProperty 方法,傳入 target 構造函數原型對象、propertyName 屬性名、initialValue 初始值和 enhancer ( deepEnhancer )。

function defineObservableProperty(target, propName, newValue, enhancer) {
    var adm = asObservableObject(target);
}

asObservableObject

asObservableObject 方法會首先判斷原型對象是否可擴展,如果不可以,則報錯。其次根據一定規則生成 name,通過調用 new ObservableObjectAdministration(target, name, defaultEnhancer) 生成 adm 對象,此對象會綁在原型對象的 $mobx 上,並返回新生成的 adm 對象。

defineObservableProperty 首先會通過調用 asObservableObject 方法獲取 adm 對象,判斷原型對象上的屬性是否可配置與可寫,如果不可以,報錯。判斷新生成的 adm 對象上是否存在 interceptors 屬性,且屬性值得長度大於 0

如果不存在,則給 adm 對象的 values 屬性賦值,值爲 ObservableValue 的實例。

ObservableValue

ObservableValue 類繼承 Atom 類,在實例化 ObservableValue 同時,會執行 enhancer 方法,在這裏即爲 deepEnhancer

var ObservableValue = (function(_super) {
    __extends(ObservableValue, _super);
    // 部分代碼
    var _this = _super.call(this, name) || this;
    _this.value = enhancer(value, undefined, name);
})(Atom);

deepEnhancer 會對原型對象進行判斷,如果是 observable,直接返回原型對象;如果是數組,返回 observable.array 調用後結果;如果是對象,返回 observable.object 調用後結果;如果是 Map,返回 observable.map 調用後結果;如果是 Set,返回 observable.set 調用後結果;如果都不是,則直接返回傳進來的 v

function deepEnhancer(v, _, name) {
    if (isObservable(v)) return v;
    if (Array.isArray(v)) return observable.array(v, { name: name });
    if (isPlainObject(v)) return observable.object(v, undefined, { name: name });
    if (isES6Map(v)) return observable.map(v, { name: name });
    if (isES6Set(v)) return observable.set(v, { name: name });
    return v;
}

defineObservableProperty 方法最後會覆蓋原型對象原有的屬性描述符,並劫持 getset 操作。

Object.defineProperty(target, propName, generateComputedPropConfig(propName));

function generateComputedPropConfig(){
    // 部分
    return {
        configurable: true,
        enumerable: true,
        get: function() {
            return this.$mobx.read(this, propName);
        },
        set: function(v) {
            this.$mobx.write(this, propName, v);
        }
    }
}

如果覺得文章不錯,對你有幫助,煩請點贊。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章