Mobx 源码初探 - observable(一)
observable 同时支持 decorator 方式和方法调用方式。
// 示例 @observable name = '张三'; const temp = observable.box(20);
在源码中可以发现 mobx 为 observable 上绑定了很多方法。
Object.keys(observableFactories).forEach(function(name) {
return (observable[name] = observableFactories[name]);
});本篇文章重点介绍 @observable 调用,其余的后续文章会继续讲解。@observable 有一个很强大的功能,它会对于不同类型,应用不同的转换规则。它究竟是如何处理的?
createObservable
当我们使用 @observable,实质上是调用了 createObservable 的返回的 deepDecorator 函数。
createObservable 接收 3 个参数 v、arg2、arg3,这三个参数分别对应构造函数的原型对象、属性名、描述符。
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 会作为 res 的 enhancer 属性存在。
createPropDecorator
createDecoratorForEnhancer 方法内部会通过 createPropDecorator 函数生成 res。createPropDecorator 函数接收 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 存在。
尤其需要注意的是,描述符中有 get 和 set 方法,这两个方法内部都会首先调用 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 方法最后会覆盖原型对象原有的属性描述符,并劫持 get 和 set 操作。
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);
}
}
}如果觉得文章不错,对你有帮助,烦请点赞。