總體會寫得比較亂,同時也比較簡單,和讀書筆記差很少,基本是邊讀邊寫。見諒~javascript
主要三大部分Atom
、Observable
、Derivation
java
Mobx的原子類,可以被觀察和通知變化,observableValue繼承於Atom。observableValue ---> Atomreact
同時裏面有幾個比較重要的屬性與方法。數組
屬性安全
方法ide
Observable是一個工廠函數,讓數據變得可觀察。這個東西須要和上述的Atom創建聯繫,即將具體的值與Atom聯繫起來。從而打通自身可以被觀察,同時能通知變化的整個流程。函數
三種可被觀察的數據類型:對象,數組,Map,下面簡單介紹如何實現。假如都不是,就會提示用戶調用observable.box,使其擁有get,set方法,即上述說的observableValue數據類型。優化
部分代碼以下:ui
fucntion Observable(v) { // 若是已被觀察,直接返回 if (isObservable(v)) return v // 根據其類型分別調用observable.object、observable.array、observable.map const res = isPlainObject(v) ? observable.object(v, arg2, arg3) : Array.isArray(v) ? observable.array(v, arg2) : isES6Map(v) ? observable.map(v, arg2) : v // 返回被觀察過的東西 if (res !== v) return res // 都不是,提示用戶調用observable.box(value) fail( process.env.NODE_ENV !== "production" && `The provided value could not be converted into an observable. If you want just create an observable reference to the object use 'observable.box(value)'` ) }
重點是observable.object、observable.array、observable.map三者的實現,下面是討論關於對象的實現方式this
對象(observable.object)
有計算屬性時,會新建一個既有observableValue也有derivation屬性的computedValue類,存放到adm[$mobx].values裏面,key就是computed的key
而後,重點是建立proxy時的handler對象的get和set函數,在有新屬性訪問時或改變值時會調用get和set函數
(observableValue簡稱爲oV,Object.defineProperty簡稱爲Od)
這樣子,整個對象屬性的監聽流程就創建起來了
Reaction(反應)是一類特殊的Derivation,能夠註冊響應函數,使之在條件知足時自動執行。使用以下:
// new Reaction(name, onInvalidate) const reaction = new Reaction('name', () => { // do something,即響應函數,發生反作用的地方 console.log('excuted!') }) const ob = observable.object({ name: 'laji', key: '9527' }) reaction.track(() => { // 註冊須要被追蹤的變量,這裏訪問了已經被觀察的ob對象,因此當ob.name或ob.key發生改變時,上面的響應函數會執行 console.log(`my name is ${ob.name}`) console.log(`${ob.key} hey hey hey!`) }) ob.name = 'mike' // 'excuted!'
讓咱們分析一下源碼實現,主要有幾個重點:
在調用track函數時,這個是重點,會調用trackDerivedFunction(this, fn, undefined)
export function trackDerivedFunction<T>(derivation: IDerivation, f: () => T, context: any) { // 將該 Derivation 的 dependenciesState 和當前全部依賴的 lowestObserverState 設爲最新的狀態 changeDependenciesStateTo0(derivation) // 創建一個該derivation新的newObserving數組,裏面存放的是誰被該derivation註冊依賴了 derivation.newObserving = new Array(derivation.observing.length + 100) // 記錄新的依賴的數量 derivation.unboundDepsCount = 0 // 每次執行都分配一個全局的 uid derivation.runId = ++globalState.runId // 重點,將當前的derivation分配爲全局的globalState.trackingDerivation,這樣被觀察的 Observable 在其 reportObserved 方法中就能獲取到該 Derivation const prevTracking = globalState.trackingDerivation globalState.trackingDerivation = derivation let result // 下面運行存入track的函數,觸發被觀察變量的get方法 if (globalState.disableErrorBoundaries === true) { result = f.call(context) } else { try { result = f.call(context) } catch (e) { result = new CaughtException(e) } } globalState.trackingDerivation = prevTracking // 比較新舊依賴,更新依賴 bindDependencies(derivation) return result }
能夠看到,重點有兩個,一個是將當前的derivation分配爲全局的globalState.trackingDerivation,一個是下面的更新依賴過程。
接下來,咱們看看觸發了被觀察變量的get方法,會是怎樣的,上面說過,調用get方法會執行reportObserved函數
export function reportObserved(observable: IObservable): boolean { // 拿到剛纔被設置到全局的derivation const derivation = globalState.trackingDerivation if (derivation !== null) { if (derivation.runId !== observable.lastAccessedBy) { observable.lastAccessedBy = derivation.runId // 這行是重點,將被觀察的變量,放到derivation.newObserving數組中,所以,derivation裏就存放了此次訪問中被觀察的變量 derivation.newObserving![derivation.unboundDepsCount++] = observable if (!observable.isBeingObserved) { observable.isBeingObserved = true observable.onBecomeObserved() } } return true } else if (observable.observers.size === 0 && globalState.inBatch > 0) { queueForUnobservation(observable) } return false }
以後是bindDependencies函數的執行。這裏面有兩點,不作代碼解讀了:
此時會調用observable的set函數,而後調用reportChanged,最終會調用一個叫作propagateChanged函數。
export function propagateChanged(observable: IObservable) { // 已經在運行了,直接返回 if (observable.lowestObserverState === IDerivationState.STALE) return observable.lowestObserverState = IDerivationState.STALE // 上面說過,observable(被觀察的變量)的observers存放着derivation // 這裏就是執行每一個derivation的onBecomeStale函數 observable.observers.forEach(d => { if (d.dependenciesState === IDerivationState.UP_TO_DATE) { if (d.isTracing !== TraceMode.NONE) { logTraceInfo(d, observable) } d.onBecomeStale() } d.dependenciesState = IDerivationState.STALE }) }
onBecomeStale最終會調用derivation裏的schedule函數,裏面作了兩件事:
執行runReactions函數
至此,整個mobx的數據觀察與響應流程就都一一解釋完整了(autorun,autorunAsync,when等函數都是基於Reaction來實現的,就不做過多解讀了)
既然mobx都說了,那就把mobx-react也分析一下吧。其實很簡單,只要理解了Reaction與Observable,就很容易明白mobx-react的實現了。
mobx-react的實現主要也是兩點
第一點比較簡單,實現一個hoc,把observerableStore添加到context上,而後被inject的組件就能夠拿到所需的observerableStore
咱們重點看下第二點,實現第二點的主要邏輯,在observer.js
裏面的makeComponentReactive函數中,看下面簡化版的重點解析
// makeComponentReactive function makeComponentReactive(render) { if (isUsingStaticRendering === true) return render.call(this) // 改造後的render函數 function reactiveRender() { // 防止重複執行響應函數,由於componentWillReact有可能有反作用 isRenderingPending = false // render函數執行後返回的jsx let rendering = undefined // 註冊須要被追蹤的變量 reaction.track(() => { if (isDevtoolsEnabled) { this.__$mobRenderStart = Date.now() } try { // _allowStateChanges是安全地執行原來的render函數,假如在action外有更改變量的行爲,會報錯 // 重點是這個,由於render函數被執行了,因此假如裏面有被observe過的變量,就能被追蹤,更新到依賴該reaction的依賴列表裏面 rendering = _allowStateChanges(false, baseRender) } catch (e) { exception = e } if (isDevtoolsEnabled) { this.__$mobRenderEnd = Date.now() } }) return rendering } // ....省略一些代碼 // 新建一個Reaction,註冊響應函數 const reaction = new Reaction(`${initialName}#${rootNodeID}.render()`, () => { if (!isRenderingPending) { // 正在執行響應函數 isRenderingPending = true // 這裏就是執行新的componentWillReact生命週期的地方 if (typeof this.componentWillReact === "function") this.componentWillReact() if (this.__$mobxIsUnmounted !== true) { let hasError = true try { setHiddenProp(this, isForcingUpdateKey, true) // 也是重點,經過forceUpdate,更新組件 if (!this[skipRenderKey]) Component.prototype.forceUpdate.call(this) hasError = false } finally { setHiddenProp(this, isForcingUpdateKey, false) if (hasError) reaction.dispose() } } } }) // 改寫原來的render reaction.reactComponent = this reactiveRender[mobxAdminProperty] = reaction this.render = reactiveRender return reactiveRender.call(this) }
能夠見到,經過創建一個Reaction,實現了render函數裏的被觀察的變量收集及響應函數註冊。並且在經過forceUpdate從新更新組件後,render函數會被從新執行一遍,從而實時更新被觀察的變量。總體的實現仍是巧妙的。
除此以外,還有一些生命週期的優化,對props、state也進行監聽等操做,在這裏就不一一解讀了