Mobx 源码分析 - shallowBox、box、ObservableValue
上文提到,observable 上绑定了 13 个方法。此篇文章会重点讲解 shallowBox 和 box。
shallowBox、box
当调用 observable.shallowBox 时,mobx 会给出废弃警告,并帮你转换成 observable.box,只是在转换的时候,会给 observable.box 传入第二个参数 { name, deep: false },这个参数的作用便是告诉 observable.box 在实例化 ObservableValue 时,使用 referenceEnhancer。
referenceEnhancer 禁用自动的 observable 转换,只是创建一个 observable 引用。


相反的,如果我们调用 observable.box,并给其传入 { deep: true }。

可以看到,value 这时已经是 ObservableValue 的实例。
打开 types/observablevalue.ts 文件,该文件向外界暴露 ObservableValue 类和 isObservableValue 方法。
ObservableValue
构造函数
ObservableValue类继承Atom类。ObservableValue类的构造函数接收 5 个参数,分别为value、enhancer、name、notifySpy和equals,其中name与equals存在默认值。name没什么好说的,就是一个id,equals的默认值为comparer.default,comparer.default主要用来判断两个值是否相等。- 调用下父类
Atom构造函数,并把value值设为传递进来的enhancer函数执行的结果。 - 判断是否需要监听和全局的监听器
spy数量,如果有,则发送一个事件。 - 类中定义了许多方法,比如
dehanceValue、set、prepareNewValue、setNewValue、get、intercept、observe、toJSON、toString和valueOf。 - 类的原型上会针对对象转成原始值时,调用
valueOf方法。
ObservableValue.prototype[primitiveSymbol()] = ObservableValue.prototype.valueOf
方法
ObservableValue 类 8 个公共方法,2 个私有方法。

observe
接收一个回调函数 listener 和是否立即调用 fireImmediately。如果 fireImmediately 为 true,则立即调用 listener 函数。
调用 registerListener 注册监听器,方法内部大部分与 registerInterceptor 实现相同。
intercept
用来在任何变化应用前将其拦截。
方法内部调用 registerInterceptor,registerInterceptor 函数内部会判断实例上是否存在 interceptors,如果不存在,则赋为 [],并把 handler 存入 interceptors。最后返回一个函数,且该函数只能被调用一次。
export function registerInterceptor<T>(
interceptable: IInterceptable<T>,
handler: IInterceptor<T>
): Lambda {
...
return once(() => {
const idx = interceptors.indexOf(handler)
if (idx !== -1) interceptors.splice(idx, 1)
})
}
export function once(func: Lambda): Lambda {
let invoked = false
return function() {
if (invoked) return
invoked = true
return (func as any).apply(this, arguments)
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
get
返回当前值。
// types/observablevalue.ts
public get(): T {
this.reportObserved()
return this.dehanceValue(this.value)
}
// core/atom.ts
public reportObserved(): boolean {
return reportObserved(this)
}
// core/observable.ts
function reportObserved(observable){}
2
3
4
5
6
7
8
9
10
11
reportObserved
首先获取全局对象中
trackingDerivation,并赋给derivation。以autorun举例,当使用autorun的时候,会在全局对象中trackingDerivation赋实例化后的Reaction判断
derivation是否有值- 有,判断
derivation上runId与实例的lastAccessedBy是否相等,相等则代表依赖已经建立过,不需要再次创建,没有则把derivation.runId赋给lastAccessedBy,然后把实例放入derivation。判断当前实例是否已处于监听状态,如果已经处于,则返回true,如果不是则把实例上isBeingObserved值改为true,并调用实例的onBecomeObserved方法 - 无,且实例上无
observers,当前已处于事务中,则把实例推入全局不再被观察队列pendingUnobservations - 无,且实例上有
observers或者当前未处于事务中,直接返回false
- 有,判断
set
替换当前存储的值并通知所有观察者。

- 方法内部首先取到以前的值
oldValue,然后调用prepareNewValue方法生成新值newValue。 - 判断
newValue是否等于globalState.UNCHANGED,如果等于什么都不做,如果不等于,则判断是否有监听器spy,有则发送spyReportStart事件,没有则什么都不做。 - 调用
setNewValue方法 - 有监听器则发送
spyReportEnd事件
toJSON
返回 this.get()
setNewValue
- 原来的值更新成传入的值
- 调用
reportChanged函数 - 判断是否有监听器
listener,如果有,调用notifyListeners方法
public reportChanged() {
// 开始处理事务,把全局 inBatch 值 + 1
startBatch();
// derivation、reaction
propagateChanged(this);
// 结束处理事务
endBatch();
}
2
3
4
5
6
7
8
propagateChanged
function propagateChanged(observable) {}
- 判断当前
observable.lowestObserverState与IDerivationState.STALE是否相等,相等则什么也不做。IDerivationState.STALE意味着自从最后一次的计算和衍生以来,值已经发生改变,下一次需要的时候需要重新计算 IDerivationState.STALE赋值给observable.lowestObserverState- 取到
observable.observers,如果有,则遍历每一项,并判断每一项的dependenciesState与IDerivationState.UP_TO_DATE是否相等。如果不等,则把每一项的dependenciesState全都设置为IDerivationState.STALE。否则调用onBecomeStale,执行所有的reaction
endBatch
判断 --global.inBatch 是否等于 0,如果是则什么也不做。否则,执行所有的 reaction。获取全局所有待取消观察的列表,把每一项 isPendingUnobservation 都设置为 false。如果 observable.observers.length 为 0,且 observable 处于观察状态,则标记 observable 为不可观察。如果 observable 为 ComputedValue 的实例,则调用 observable.suspend,最后清空全局所有待取消观察列表。
toString
返回 ${this.name}[${this.value}]
valueOf
返回 toPrimitive(this.get())
dehanceValue
如果有 dehancer,则返回调用 dehancer 方法后的值,否则直接返回值
prepareNewValue
值得一提的是,方法内部会首先判断是否有拦截器 interceptor。
如果有则会依次调用拦截器方法,并判断拦截器是否返回对象或 nothing,如果不是则报错;如果是 nothing,则直接返回,下面的拦截器不再调用;如果是对象,则继续调用接下来的拦截器。
export function interceptChange<T>( interceptable: IInterceptable<T | null>, change: T | null ): T | null { const prevU = untrackedStart() try { const interceptors = interceptable.interceptors if (interceptors) for (let i = 0, l = interceptors.length; i < l; i++) { change = interceptors[i](change) invariant( !change || (change as any).type, "Intercept handlers should return nothing or a change object" ) if (!change) break } return change } finally { untrackedEnd(prevU) } }1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21如果返回的
change为 nothing,则直接返回globalState.UNCHANGED,否则取change.newValue作为新值。接下来方法和没有拦截器走的逻辑一致。如果没有拦截器,则调用
enhancer方法生成新值,最后判断旧值与新值是否相等,如果相等返回globalState.UNCHANGED,否则返回新值。
isObservableValue
isObservableValue 函数是 createInstanceofPredicate 函数返回值。
var isObservableValue = createInstanceofPredicate("ObservableValue", ObservableValue);
结尾语
mox 源码阅读起来,很难理解,需要细细品尝。如果觉得写得好不错,还请鼓励下,点个👍。