共计 4213 个字符,预计需要花费 11 分钟才能阅读完成。
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);
}
}
}
如果觉得文章不错,对你有帮助,烦请点赞。