Mobx 源码分析 - shallowBox、box、ObservableValue

上文提到,observable 上绑定了 13 个方法。此篇文章会重点讲解 shallowBoxbox

shallowBox、box

当调用 observable.shallowBox 时,mobx 会给出废弃警告,并帮你转换成 observable.box,只是在转换的时候,会给 observable.box 传入第二个参数 { name, deep: false },这个参数的作用便是告诉 observable.box 在实例化 ObservableValue 时,使用 referenceEnhancer

referenceEnhancer 禁用自动的 observable 转换,只是创建一个 observable 引用。

img

img

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

img

可以看到,value 这时已经是 ObservableValue 的实例。

打开 types/observablevalue.ts 文件,该文件向外界暴露 ObservableValue 类和 isObservableValue 方法。

ObservableValue

构造函数

  1. ObservableValue 类继承 Atom 类。
  2. ObservableValue 类的构造函数接收 5 个参数,分别为 valueenhancernamenotifySpyequals,其中 nameequals 存在默认值。name没什么好说的,就是一个 idequals 的默认值为 comparer.defaultcomparer.default 主要用来判断两个值是否相等。
  3. 调用下父类 Atom 构造函数,并把 value 值设为传递进来的 enhancer 函数执行的结果。
  4. 判断是否需要监听和全局的监听器 spy 数量,如果有,则发送一个事件。
  5. 类中定义了许多方法,比如 dehanceValuesetprepareNewValuesetNewValuegetinterceptobservetoJSONtoStringvalueOf
  6. 类的原型上会针对对象转成原始值时,调用 valueOf 方法。
ObservableValue.prototype[primitiveSymbol()] = ObservableValue.prototype.valueOf
1

方法

ObservableValue 类 8 个公共方法,2 个私有方法。

img

observe

接收一个回调函数 listener 和是否立即调用 fireImmediately。如果 fireImmediatelytrue,则立即调用 listener 函数。

调用 registerListener 注册监听器,方法内部大部分与 registerInterceptor 实现相同。

intercept

用来在任何变化应用前将其拦截。

方法内部调用 registerInterceptorregisterInterceptor 函数内部会判断实例上是否存在 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)
    }
}
1
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){}
1
2
3
4
5
6
7
8
9
10
11
reportObserved
  1. 首先获取全局对象中 trackingDerivation,并赋给 derivation。以 autorun 举例,当使用 autorun 的时候,会在全局对象中 trackingDerivation 赋实例化后的 Reaction

  2. 判断 derivation 是否有值

    • 有,判断 derivationrunId 与实例的 lastAccessedBy 是否相等,相等则代表依赖已经建立过,不需要再次创建,没有则把 derivation.runId 赋给 lastAccessedBy,然后把实例放入 derivation。判断当前实例是否已处于监听状态,如果已经处于,则返回 true,如果不是则把实例上 isBeingObserved 值改为 true,并调用实例的 onBecomeObserved 方法
    • 无,且实例上无 observers ,当前已处于事务中,则把实例推入全局不再被观察队列 pendingUnobservations
    • 无,且实例上有 observers 或者当前未处于事务中,直接返回 false

set

替换当前存储的值并通知所有观察者。

img

  1. 方法内部首先取到以前的值 oldValue,然后调用 prepareNewValue 方法生成新值 newValue
  2. 判断 newValue 是否等于 globalState.UNCHANGED,如果等于什么都不做,如果不等于,则判断是否有监听器 spy,有则发送 spyReportStart 事件,没有则什么都不做。
  3. 调用 setNewValue 方法
  4. 有监听器则发送 spyReportEnd 事件

toJSON

返回 this.get()

setNewValue

  1. 原来的值更新成传入的值
  2. 调用 reportChanged 函数
  3. 判断是否有监听器 listener,如果有,调用 notifyListeners 方法
public reportChanged() {
    // 开始处理事务,把全局 inBatch 值 + 1
    startBatch();
    // derivation、reaction
    propagateChanged(this);
    // 结束处理事务
    endBatch();
}
1
2
3
4
5
6
7
8
propagateChanged
function propagateChanged(observable) {}
1
  1. 判断当前 observable.lowestObserverStateIDerivationState.STALE 是否相等,相等则什么也不做。IDerivationState.STALE 意味着自从最后一次的计算和衍生以来,值已经发生改变,下一次需要的时候需要重新计算
  2. IDerivationState.STALE 赋值给 observable.lowestObserverState
  3. 取到 observable.observers,如果有,则遍历每一项,并判断每一项的 dependenciesStateIDerivationState.UP_TO_DATE 是否相等。如果不等,则把每一项的 dependenciesState 全都设置为 IDerivationState.STALE。否则调用 onBecomeStale,执行所有的 reaction
endBatch

判断 --global.inBatch 是否等于 0,如果是则什么也不做。否则,执行所有的 reaction。获取全局所有待取消观察的列表,把每一项 isPendingUnobservation 都设置为 false。如果 observable.observers.length0,且 observable 处于观察状态,则标记 observable 为不可观察。如果 observableComputedValue 的实例,则调用 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);
1

结尾语

mox 源码阅读起来,很难理解,需要细细品尝。如果觉得写得好不错,还请鼓励下,点个👍。

最后更新: 8/25/2019, 9:32:02 PM