Mobx 源码分析 - 第一阶段汇总
当我们写
age = observable.box(12)的时候,写@observable age = 12的时候,写age = observable(12)的时候,mobx做了什么?不知道有多少人仔细研究过,没研究也不要紧,这篇文章就是带你去了解,mobx具体做了什么?
api/observable.ts 文件
observable.box
observable 上绑定了许多属性,比如常用的 box、map 和 array。当我们调用 observable.box 的时候,mobx 会首先判断当前调用方式,如果当前的调用方式会 decorator 调用,则直接报错。
if (arguments.length > 2) incorrectlyUsedAsDecorator("box")
为什么 mobx 可以通过参数个数就能判断出来当前是否为 decorator 调用?那是因为如果通过装饰器调用,会往函数内传入 3 个参数,所以这边通过判断参数个数就可以确定当前是否为 decorator 调用。
相信大家都知道 box 方法还有第二个参数 (不仅仅只是 box 有),这个参数的作用主要用以判断用哪一个 enhancer 和 name 的值。
最后返回 ObservableValue 实例。
new ObservableValue(value, getEnhancerFromOptions(o), o.name, true, o.equals)
@observable
调用 observable 时候,等于调用 createObservable。
const observable = createObservable
createObservable 接收 3 个参数,内部会对第二个参数进行判断,如果为 string,则调用 deepDecorator 函数。
observable()
如果传入的参数为已监听对象,则直接返回;如果不是基本数据类型,则根据参数类型调用不同的包装方法,比如对于 array 调用 observable.array。如果是 NaN,直接返回。如果是基本数据类型,提醒用户使用 observable.box 方法。
我们已经知道 mobx 对于我们的代码会做两件不同的事,一种调用 deepDecorator 函数,一种实例化 ObservableValue。
注意,这里我们只针对于传入的值为
number类型讨论,不同类型有不同的实现方式
observable.box
实例化 ObservableValue,ObservableValue 类继承 Atom 类,ObservableValue 内部重写了 set 和 get 方法。
当访问实例时,会向全局发出 reportObserved 事件,并把当前实例存到 derivation.newObserving 中,以便数据发生更改,通知 derivation。在这里,实例为被观察者,derivation 为观察者。
修改实例值时,会开启事务处理,并向所有的观察者推送此次事件,推送完成后结束事务处理。
究竟 mobx 是如何更新依赖关系?我们以 autorun 举例。
autorun(()=>{
console.log(age.get())
})
2
3
当我们调用 autorun 时候,会在全局状态中增加当前实例,也就是当前的 derivation,autorun 参数执行时,会把 age 的值,也就是 ObservableValue 实例,推入到当前的 derivation 属性中,derivation 会用 diffValue 来刷新当前依赖关系,保持依赖最新。
@observable
调用 createDecoratorForEnhancer 返回值,在原型上添加 __mobxDecorators 对象,此对象的每一个属性 ( 比方说 @observable age,这里面的属性就是 age ) 都会存放一些关键信息,并劫持属性的 get 与 set 方法。
target.__mobxDecorators[prop] = {
prop,
propertyCreator,
descriptor,
decoratorTarget: target,
decoratorArguments
}
2
3
4
5
6
7
当访问属性,会在原型上添加 __mobxDidRunLazyInitializers 和 $mobx。$mobx 内存放 ObservableObjectAdministration 实例,并在 $mobx.values 对应的每一个属性存放 ObservableValue 实例。
const observable = (adm.values[propName] = new ObservableValue(
newValue,
enhancer,
`${adm.name}.${propName}`,
false
))
2
3
4
5
6
并对于属性的访问和读取会再次劫持,每当访问,都会调用原型上 $mobx 中 read 和 write 方法。read 和 write 方法所做的事与 observable.box 内部 get 和 set 做的事情一样。