Mobx——derivation 的派發(fā)更新過程 (離職拷貝版)

離職了,把 2019 年在公司寫的文檔 copy 出來。年頭有點(diǎn)久链烈,可能寫的不太對,也不是很想改了~
注:本文檔對應(yīng) mobx 版本為 4.15.4挚躯、mobx-vue 版本為 2.0.10

寫在前面

簡易demo 代碼如下:


然后我們在執(zhí)行前打印一下這個 Store 的實(shí)例:


結(jié)合上面幾部分的源碼分析强衡,@observable 的變量和 @computed 的變量會在 $mobx 也就是 那個統(tǒng)管整個 mobx 的 ObservableObjectAdministration 實(shí)例的 values 上,而在 reaction 里面聲明的 Reaction秧均,會在相關(guān) ObservableValue 的 observers 下面食侮。比如這個例子号涯,value 變化需要通知三個東西目胡,一個是視圖的重新渲染,一個是 computed 變量的計(jì)算链快、一個是 Reaction 的執(zhí)行:

而 squared 下面的 observers 只有一個重新渲染視圖的 Reaction

源碼解析

1. 修改變量 this.value++

根據(jù) @observers 的源碼誉己,當(dāng) @observable 變量被改變時,會觸發(fā) set 函數(shù)

set(v) {
    this.$mobx.write(this, propName, v)
}

2. ObservableObjectAdministration.write

write(owner: any, key: string, newValue) {
    const instance = this.target
    if (process.env.NODE_ENV === "production" && instance !== owner) {
        this.illegalAccess(owner, key)
    }
    const observable = this.values[key]
    if (observable instanceof ComputedValue) {
        observable.set(newValue)
        return
    }

    // intercept
    if (hasInterceptors(this)) {
        const change = interceptChange<IObjectWillChange>(this, {
            type: "update",
            object: instance,
            name: key,
            newValue
        })
        if (!change) return
        newValue = (change as any).newValue
    }
    newValue = (observable as any).prepareNewValue(newValue)

    // notify spy & observers
    if (newValue !== globalState.UNCHANGED) {
        const notify = hasListeners(this)
        const notifySpy = isSpyEnabled()
        const change =
            notify || notifySpy
                ? {
                        type: "update",
                        object: instance,
                        oldValue: (observable as any).value,
                        name: key,
                        newValue
                    }
                : null


        if (notifySpy) spyReportStart({ ...change, name: this.name, key })
        ;(observable as ObservableValue<any>).setNewValue(newValue)
        if (notify) notifyListeners(this, change)
        if (notifySpy) spyReportEnd()
    }
}

3. observable.setNewValue

這里看的是 @observable number 也就是 observableValue 的對應(yīng)操作域蜗,話說還有直接改變 computedValue 的時候巨双?@computed 不是都是 get 的只讀屬性?

setNewValue(newValue: T) {
    const oldValue = this.value
    this.value = newValue
    this.reportChanged()
    if (hasListeners(this)) {
        notifyListeners(this, {
            type: "update",
            object: this,
            newValue,
            oldValue
        })
    }
}

observable 繼承自 Atom霉祸,所以 reportChanged 是 Atom 的成員函數(shù)

4. Atom.reportChanged

public reportChanged() {
    startBatch()
    propagateChanged(this)
    endBatch()
}

5. propagateChanged

function propagateChanged(observable: IObservable) {
    if (observable.lowestObserverState === IDerivationState.STALE) return
    observable.lowestObserverState = IDerivationState.STALE

    const observers = observable.observers
    let i = observers.length
    while (i--) {
        const d = observers[i]
        if (d.dependenciesState === IDerivationState.UP_TO_DATE) {
            if (d.isTracing !== TraceMode.NONE) {
                logTraceInfo(d, observable)
            }
            d.onBecomeStale()
        }
        d.dependenciesState = IDerivationState.STALE
    }
}

此時 observers 就是上述截圖 log 里面那三個:



ComputedValue 和 Reaction 的 onBecomStale 有些不同筑累,源碼分別為:

// ComputedValue
onBecomeStale() {
    propagateMaybeChanged(this)
}

function propagateMaybeChanged(observable: IObservable) {
    // invariantLOS(observable, "maybe start");
    if (observable.lowestObserverState !== IDerivationState.UP_TO_DATE) return
    observable.lowestObserverState = IDerivationState.POSSIBLY_STALE

    const observers = observable.observers
    let i = observers.length
    while (i--) {
        const d = observers[i]
        if (d.dependenciesState === IDerivationState.UP_TO_DATE) {
            d.dependenciesState = IDerivationState.POSSIBLY_STALE
            if (d.isTracing !== TraceMode.NONE) {
                logTraceInfo(d, observable)
            }
            d.onBecomeStale()
        }
    }
}

// Reaction
onBecomeStale() {
    this.schedule()
}

// 這里的邏輯和 action事務(wù)特性那一節(jié)的邏輯重疊了,就不再過了
schedule() {
    if (!this._isScheduled) {
        this._isScheduled = true
        globalState.pendingReactions.push(this)
        runReactions()
    }
}

結(jié)論

前半段丝蹭,派發(fā)更新的流程都是一樣的慢宗,到了 propagateChanged的時候,Reaction 和 ComputedValue 發(fā)生了分歧:

Reaction 的就比較直接,大致就是 action 事務(wù)特性那一節(jié)里面講的镜沽,塞 pendingReactions敏晤,然后再一個一個執(zhí)行掉。

ComputedValue 則是套了一層缅茉,看看在 ComputedValue 上的 observers 里有哪些 IDerivation嘴脾,這個 demo 里只有一個更新視圖的 Reaction。然后和 Vue 一樣蔬墩, 重復(fù)的多余操作會被濾除译打,比如這個更新視圖的操作。實(shí)際的計(jì)算屬性回調(diào)拇颅,因?yàn)闆]有像 Reaction 那樣的 schedule 操作扶平,不會在 runReactions 的階段被觸發(fā),而是在 Vue 的 render 之后以 get 的形式被執(zhí)行蔬蕊。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末结澄,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子岸夯,更是在濱河造成了極大的恐慌麻献,老刑警劉巖,帶你破解...
    沈念sama閱讀 212,599評論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件猜扮,死亡現(xiàn)場離奇詭異勉吻,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)旅赢,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,629評論 3 385
  • 文/潘曉璐 我一進(jìn)店門齿桃,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人煮盼,你說我怎么就攤上這事短纵。” “怎么了僵控?”我有些...
    開封第一講書人閱讀 158,084評論 0 348
  • 文/不壞的土叔 我叫張陵香到,是天一觀的道長。 經(jīng)常有香客問我报破,道長悠就,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 56,708評論 1 284
  • 正文 為了忘掉前任充易,我火速辦了婚禮梗脾,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘盹靴。我一直安慰自己炸茧,他們只是感情好帆疟,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,813評論 6 386
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著宇立,像睡著了一般踪宠。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上妈嘹,一...
    開封第一講書人閱讀 50,021評論 1 291
  • 那天柳琢,我揣著相機(jī)與錄音,去河邊找鬼润脸。 笑死柬脸,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的毙驯。 我是一名探鬼主播倒堕,決...
    沈念sama閱讀 39,120評論 3 410
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼爆价!你這毒婦竟也來了垦巴?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,866評論 0 268
  • 序言:老撾萬榮一對情侶失蹤铭段,失蹤者是張志新(化名)和其女友劉穎骤宣,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體序愚,經(jīng)...
    沈念sama閱讀 44,308評論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡憔披,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,633評論 2 327
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了爸吮。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片芬膝。...
    茶點(diǎn)故事閱讀 38,768評論 1 341
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖形娇,靈堂內(nèi)的尸體忽然破棺而出锰霜,到底是詐尸還是另有隱情,我是刑警寧澤埂软,帶...
    沈念sama閱讀 34,461評論 4 333
  • 正文 年R本政府宣布锈遥,位于F島的核電站纫事,受9級特大地震影響勘畔,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜丽惶,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 40,094評論 3 317
  • 文/蒙蒙 一炫七、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧钾唬,春花似錦万哪、人聲如沸侠驯。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,850評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽吟策。三九已至,卻和暖如春的止,著一層夾襖步出監(jiān)牢的瞬間檩坚,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,082評論 1 267
  • 我被黑心中介騙來泰國打工诅福, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留匾委,地道東北人。 一個月前我還...
    沈念sama閱讀 46,571評論 2 362
  • 正文 我出身青樓氓润,卻偏偏與公主長得像赂乐,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子咖气,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,666評論 2 350

推薦閱讀更多精彩內(nèi)容