Mobx 源碼分析 - shallowBox、box、ObservableValue

上篇文章Mobx 源碼分析 - 熱身提到,observable 上綁定了 13 個方法。此篇文章會重點講解 shallowBoxboxreact

shallowBox、box

當調用 observable.shallowBox 時,mobx 會給出廢棄警告,並幫你轉換成 observable.box,只是在轉換的時候,會給 observable.box 傳入第二個參數 { name, deep: false },這個參數的做用即是告訴 observable.box 在實例化 ObservableValue 時,使用 referenceEnhancerapp

referenceEnhancer 禁用自動的 observable 轉換,只是建立一個 observable 引用。函數

相反的,若是咱們調用 observable.box,並給其傳入 { deep: true }源碼分析

能夠看到,value 這時已是 ObservableValue 的實例。post

打開 types/observablevalue.ts 文件,該文件向外界暴露 ObservableValue 類和 isObservableValue 方法。ui

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
複製代碼

方法

ObservableValue 類 8 個公共方法,2 個私有方法。this

observe

接收一個回調函數 listener 和是否當即調用 fireImmediately。若是 fireImmediatelytrue,則當即調用 listener 函數。spa

調用 registerListener 註冊監聽器,方法內部大部分與 registerInterceptor 實現相同。prototype

intercept

用來在任何變化應用前將其攔截。3d

方法內部調用 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)
    }
}
複製代碼

get

返回當前值,具體內容講解會放在後面章節。

set

替換當前存儲的值並通知全部觀察者。

  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() {
    // 開始處理事務
    startBatch();
    // derivation、reaction
    propagateChanged(this);
    // 結束處理事務
    endBatch();
}
複製代碼

toString

返回 ${this.name}[${this.value}]

valueOf

返回 toPrimitive(this.get())

dehanceValue

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)
        }
    }
    複製代碼

    若是返回的 change 爲 nothing,則直接返回 globalState.UNCHANGED,不然取 change.newValue 做爲新值。接下來方法和沒有攔截器走的邏輯一致。

  • 若是沒有攔截器,則調用 enhancer 方法生成新值,最後判斷舊值與新值是否相等,若是相等返回 globalState.UNCHANGED,不然返回新值。

isObservableValue

isObservableValue 函數是 createInstanceofPredicate 函數返回值。

var isObservableValue = createInstanceofPredicate("ObservableValue", ObservableValue);
複製代碼

結尾語

mox 源碼閱讀起來,很難理解,須要細細品嚐。若是以爲寫得好不錯,還請鼓勵下,點個👍。

相關文章
相關標籤/搜索