Mobx 源码分析 - 第一阶段汇总

当我们写 age = observable.box(12) 的时候,写 @observable age = 12 的时候,写 age = observable(12) 的时候,mobx 做了什么?不知道有多少人仔细研究过,没研究也不要紧,这篇文章就是带你去了解,mobx 具体做了什么?

api/observable.ts 文件

observable.box

observable 上绑定了许多属性,比如常用的 boxmaparray。当我们调用 observable.box 的时候,mobx 会首先判断当前调用方式,如果当前的调用方式会 decorator 调用,则直接报错。

if (arguments.length > 2) incorrectlyUsedAsDecorator("box")
1

为什么 mobx 可以通过参数个数就能判断出来当前是否为 decorator 调用?那是因为如果通过装饰器调用,会往函数内传入 3 个参数,所以这边通过判断参数个数就可以确定当前是否为 decorator 调用。

相信大家都知道 box 方法还有第二个参数 (不仅仅只是 box 有),这个参数的作用主要用以判断用哪一个 enhancername 的值。

最后返回 ObservableValue 实例。

new ObservableValue(value, getEnhancerFromOptions(o), o.name, true, o.equals)
1

@observable

调用 observable 时候,等于调用 createObservable

const observable = createObservable
1

createObservable 接收 3 个参数,内部会对第二个参数进行判断,如果为 string,则调用 deepDecorator 函数。

observable()

如果传入的参数为已监听对象,则直接返回;如果不是基本数据类型,则根据参数类型调用不同的包装方法,比如对于 array 调用 observable.array。如果是 NaN,直接返回。如果是基本数据类型,提醒用户使用 observable.box 方法。


我们已经知道 mobx 对于我们的代码会做两件不同的事,一种调用 deepDecorator 函数,一种实例化 ObservableValue

注意,这里我们只针对于传入的值为 number 类型讨论,不同类型有不同的实现方式

observable.box

实例化 ObservableValueObservableValue 类继承 Atom 类,ObservableValue 内部重写了 setget 方法。

当访问实例时,会向全局发出 reportObserved 事件,并把当前实例存到 derivation.newObserving 中,以便数据发生更改,通知 derivation。在这里,实例为被观察者,derivation 为观察者。

修改实例值时,会开启事务处理,并向所有的观察者推送此次事件,推送完成后结束事务处理。

究竟 mobx 是如何更新依赖关系?我们以 autorun 举例。

autorun(()=>{
    console.log(age.get())
})
1
2
3

当我们调用 autorun 时候,会在全局状态中增加当前实例,也就是当前的 derivationautorun 参数执行时,会把 age 的值,也就是 ObservableValue 实例,推入到当前的 derivation 属性中,derivation 会用 diffValue 来刷新当前依赖关系,保持依赖最新。

@observable

调用 createDecoratorForEnhancer 返回值,在原型上添加 __mobxDecorators 对象,此对象的每一个属性 ( 比方说 @observable age,这里面的属性就是 age ) 都会存放一些关键信息,并劫持属性的 getset 方法。

target.__mobxDecorators[prop] = {
    prop,
    propertyCreator,
    descriptor,
    decoratorTarget: target,
    decoratorArguments
}
1
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
))
1
2
3
4
5
6

并对于属性的访问和读取会再次劫持,每当访问,都会调用原型上 $mobxreadwrite 方法。readwrite 方法所做的事与 observable.box 内部 getset 做的事情一样。

最后更新: 9/2/2019, 3:29:13 PM