離職了,把 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í)行蔬蕊。