Mobx 源码初探 - observable(一)

observable 同时支持 decorator 方式和方法调用方式。

// 示例
@observable name = '张三';
const temp = observable.box(20);
1
2
3

在源码中可以发现 mobxobservable 上绑定了很多方法。

Object.keys(observableFactories).forEach(function(name) {
    return (observable[name] = observableFactories[name]);
});
1
2
3

本篇文章重点介绍 @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);
    }
    ...
}
1
2
3
4
5
6
7

createDecoratorForEnhancer

deepDecorator 函数是 createDecoratorForEnhancer 的返回值,它把 deepEnhancer 作为参数传递进去。deepDecorator 的具体功能就是针对不同类型使用不同方式的 observable

var deepDecorator = createDecoratorForEnhancer(deepEnhancer);
1

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 // 此时为空对象
    });
}
1
2
3
4
5
6
7
8

当构造函数原型对象上存在 __mobxDecorators 属性,则执行下面代码。

target.__mobxDecorators[prop] = {
    prop: prop, // 属性名
    propertyCreator: propertyCreator, // 传递进来的函数
    descriptor: descriptor, // 描述符
    decoratorTarget: target, // 构造函数原型对象
    decoratorArguments: decoratorArguments // 此时为 []
};
1
2
3
4
5
6
7

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;
1
2
3
4
5

defineObservableProperty

初始值获取之后,调用 defineObservableProperty 方法,传入 target 构造函数原型对象、propertyName 属性名、initialValue 初始值和 enhancer ( deepEnhancer )。

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

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);
1
2
3
4
5
6

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;
}
1
2
3
4
5
6
7
8

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);
        }
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

如果觉得文章不错,对你有帮助,烦请点赞。

最后更新: 8/14/2019, 10:44:13 PM