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
源码阅读起来,很难理解,需要细细品尝。如果觉得写得好不错,还请鼓励下,点个👍。