前言
emm 由於實習的專案組只有自己一個人,選型開發都為所欲為了tat,從以redux進行資料流管理的使用,決定嘗試一波實戰mobx,在使用中不禁在想,mobx是怎麼和react連在一起,而mobx又是為什麼可以如此高效便捷。react-redux的驅動檢視更新是通過connect
元件,在外層HOC中訂閱store改變觸發
setState({})
進行更新檢視。而mobx-react中的observer呢?
這裡可以看到最關鍵的一點,區別於redux的更新方式,在mobx-react中是直接使用forceUpdate
方法進行更新檢視,而區別於redux 對store的訂閱,mobx-react又是怎麼樣觸發forceUpdate呢?
/**
*強制更新元件https://github.com/mobxjs/mobx-react/blob/master/src/observer.js#L188
*/
Component.prototype.forceUpdate.call(this)
複製程式碼
定位到observer,先了解observer執行的流程
核心點
talk is so cheap show you the code
//...
// https://github.com/mobxjs/mobx-react/blob/master/src/observer.js#L334
mixinLifecycleEvents(target)
//mixin元件的生命週期,將reactiveMixin中的函式mixin到具體的生命週期中.
複製程式碼
在mixin中最核心需要關注的就是componentWillMount,
先簡單提一下mixinLifecycleEvents
區別對待的是shouldComponentUpdate
,如果該方法沒有定義observer
會直接將其重寫為PureComponent
的shouldComponentUpdate
的實現
進一步看componentWillMount
//https://github.com/mobxjs/mobx-react/blob/master/src/observer.js#L168
// wire up reactive render
//儲存當前函式的render
const baseRender = this.render.bind(this)
let reaction = null
let isRenderingPending = false
const initialRender = () => {
/*繫結reaction,observable屬性改變的時候會觸發這個(mobx實質是雙向繫結,observable更新檢視也要更新,這裡是資料繫結到檢視上的第一步。)
*/
reaction = new Reaction(`${initialName}#${rootNodeID}.render()`, () => {
if (!isRenderingPending) {
// N.B. Getting here *before mounting* means that a component constructor has side effects (see the relevant test in misc.js)
// This unidiomatic React usage but React will correctly warn about this so we continue as usual
// See #85 / Pull #44
isRenderingPending = true
if (typeof this.componentWillReact === "function") this.componentWillReact() // TODO: wrap in action?
if (this.__$mobxIsUnmounted !== true) {
// If we are unmounted at this point, componentWillReact() had a side effect causing the component to unmounted
// TODO: remove this check? Then react will properly warn about the fact that this should not happen? See #73
// However, people also claim this migth happen during unit tests..
let hasError = true
try {
isForcingUpdate = true
if (!skipRender) Component.prototype.forceUpdate.call(this)
hasError = false
} finally {
isForcingUpdate = false
if (hasError) reaction.dispose()
}
}
}
})
reaction.reactComponent = this
reactiveRender.$mobx = reaction
// 重寫render
this.render = reactiveRender
// 實際的render
return reactiveRender()
}
const reactiveRender = () => {
isRenderingPending = false
let exception = undefined
let rendering = undefined
/**
* 核心關聯部分
* 追蹤資料
* https://github.com/mobxjs/mobx/blob/master/src/core/reaction.ts#L112
* reaction.track(fn: () => void)
* 實際上
* trackDerivedFunction<T>(derivation: IDerivation, f: () => T, context)
* const result = trackDerivedFunction(this, fn, undefined)// this對應reaction,fn對應track中的fn
* (https://github.com/mobxjs/mobx/blob/master/src/core/derivation.ts#L131)
* trackDerivedFunction這個函式有什麼作用?
* 執行函式f並跟蹤那些可觀察並且正在f函式中引用的變數,將這些可追蹤的變數註冊並儲存在derivation中即reaction中
*
* f中引用的變數 核心上是通過atom.reportObserved()關聯引用
* 簡單例子見 makePropertyObservableReference 中的
* get: function() {
* atom.reportObserved()
* return valueHolder
* },
*
* 重新到trackDerivedFunction執行中
* result = f.call(context);
*f本身已經是箭頭函式了,上下文已經繫結過了. 這裡實際上就是執行component的render,
*實際上就是通過這裡收集到observable的引用 (依賴收集)
*trackDerivedFunction中會derivation進行屬性更新,以及通過bindDependencies更新依賴收集的情況。
**/
reaction.track(() => {
if (isDevtoolsEnabled) {
this.__$mobRenderStart = Date.now()
}
try {
rendering = extras.allowStateChanges(false, baseRender)
} catch (e) {
exception = e
}
if (isDevtoolsEnabled) {
this.__$mobRenderEnd = Date.now()
}
})
if (exception) {
errorsReporter.emit(exception)
throw exception
}
return rendering
}
this.render = initialRender
},
複製程式碼
這一次的閱讀之行到此就暫告一段落了,關於derivation原理的進一步理解需要進一步學習mobx的原始碼。目前只是有較淺的理解,如有不正之處,請大家指出! tat 畢竟只是閉門造車。
學習資料
- http://www.cnblogs.com/rubylouvre/p/6058575.html
- https://zhuanlan.zhihu.com/p/27448262