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); } } }
如果觉得文章不错,对你有帮助,烦请点赞。