Mobx 源碼分析 - autorun

做用

當用 autorun 時,函數會被當即執行一次,而後每當它的依賴有任何改變,autorun 都會執行一次。react

源碼解析

  1. 生成 nameapi

  2. 判斷 opts 上是否有 schedulerdelay。若是都沒有,則爲 true,不然爲 false函數

    • runSync 爲 truethis

      1. 實例化 Reaction,傳入 nameonInvalidate 函數和錯誤處理函數。spa

        reaction = new Reaction(
            name,
            function(this: Reaction) {
                this.track(reactionRunner);
            },
            opts.onError
        );
        複製代碼
    • runSync 爲 falsecode

      1. 根據傳入參數,生成本身的調度 scheduler 函數server

      2. 實例化 Reaction,傳入 nameonInvalidate 函數和錯誤處理函數。對象

        reaction = new Reaction(
            name,
            () => {
                if (!isScheduled) {
                    isScheduled = true;
                    scheduler(() => {
                        isScheduled = false;
                        if (!reaction.isDisposed) reaction.track(reactionRunner);
                    });
                }
            },
            opts.onError
        );
        複製代碼
  3. this 添加到全局的待處理列表 pendingReactions事件

  4. 開始執行 runReactions事務

  5. 判斷當前全局中是否處於處理事務狀態或處理反應 reactions 階段,若是是則返回,什麼都不作。不然執行 reactionScheduler(runReactionsHelper),也就是執行 runReactionsHelper

    if (globalState.inBatch > 0 || globalState.isRunningReactions) return
        reactionScheduler(runReactionsHelper)
    複製代碼
  6. 把全局狀態中 isRunningReactions 設爲 true,經過 pendingReactions 取到全部待處理 Reaction 實例,並清空 pendingReactions 列表

  7. 遍歷全部實例,針對每個實例都調用其自身的 runReaction,遍歷結束後把全局 isRunningReactions 改成 false

    runReaction() {
            // 當前是否已經清除
            if (!this.isDisposed) {
                // 開始處理事務,設置 global.inBatch,令其值 +1
                startBatch()
                this._isScheduled = false
                // 判斷是否須要追蹤,當前 dependenciesState 處於 NOT_TRACKING,shouldCompute 會對於此狀態返回 true
                if (shouldCompute(this)) {
                    // 改變當前狀態
                    this._isTrackPending = true
    
                    try {
                        // 執行傳遞進來的函數
                        this.onInvalidate()
                        // 判斷當前 _isTrackPending 狀態和全局監聽器 spy,若是有全局監聽器,則發送事件,類型爲 scheduled-reaction
                        if (this._isTrackPending && isSpyEnabled()) {
                            // onInvalidate didn't trigger track right away..
                            spyReport({
                                name: this.name,
                                type: "scheduled-reaction"
                            })
                        }
                    } catch (e) {
                        // 錯誤處理
                        this.reportExceptionInDerivation(e)
                    }
                }
                // 結束處理事務
                endBatch()
            }
        }
    複製代碼

onInvalidate

執行 track 函數,參數爲 reactionRunner

// api/autorun.ts
function(this: Reaction) {
    this.track(reactionRunner);
}
// core/reaction.ts
function track(fn){
    startBatch()
    ...
    const result = trackDerivedFunction(this, fn, undefined)
    ...
}
// core/derivation.ts
function trackDerivedFunction(derivation, f, context){
    ...
    result = f.call(context)
    ...
    bindDependencies(derivation)
}
複製代碼
  1. 函數內部會調用 startBatch,再次使 global.inBatch+1
  2. 判斷是否有監聽器 spy,有則發送事件,類型爲 reaction,並記錄當前時間
  3. 設置當前狀態 _isRunningtrue
  4. 改變當前依賴狀態 dependenciesStateUP_TO_DATEUP_TO_DATE 意味着值是新的
  5. 取出傳入對象的 observing 屬性值,若是有值,則遍歷,把每一項的 lowestObserverState 都設爲 UP_TO_DATE
  6. 把傳入的 derivation 設置到全局對象上,同時給 derivation 新增屬性
  7. 判斷是否須要捕獲錯誤 globalState.disableErrorBoundaries,調用傳入的函數,收集新的依賴
  8. 把全局對象 trackingDerivation 恢復原狀
  9. 更新依賴關係,針對新的依賴,增長監聽者,對於舊的依賴且新的依賴中也沒有使用到的,則移除此依賴,並執行 onBecomeStale,更新全部的依賴項
  10. 根據 this.isDisposed 判斷須要清除 autorun
  11. 若有監聽器,則向監聽器發送這次 reactionderivation 耗時
  12. 結束事務處理,移除監聽者,並把清除監聽狀態
相關文章
相關標籤/搜索