標籤:

mobx-react 原理解析(二)

mobx-react 原理解析(一)

這篇是為了完成上篇留下的作業,關於action和compute的實現原理解析

compute

我們還是從es7的語法來看,從裝飾器看起@compute,

export var computed = function computed(arg1, arg2, arg3) { if (typeof arg2 === "string") { //es7寫法走這裡 return computedDecorator.apply(null, arguments); } invariant(typeof arg1 === "function", getMessage("m011")); invariant(arguments.length < 3, getMessage("m012")); const opts = typeof arg2 === "object" ? arg2 : {}; opts.setter = typeof arg2 === "function" ? arg2 : opts.setter; return new ComputedValue(arg1, opts.context, opts.compareStructural || opts.struct || false, opts.name || arg1.name || "", opts.setter); }//我們看看ComputeValue既是Observable又是Iderivation,這就是它非常獨特的地方export class ComputedValue<T> implements IObservable, IComputedValue<T>, IDerivation {//不管是怎麼創建的,最終都是創建ComputedValue,接下來我們來看看看ComputedValue的get方法ComputedValue.prototype.get = function () { invariant(!this.isComputing, "Cycle detected in computation " + this.name, this.derivation); if (globalState.inBatch === 0) { //如果不處於Reaction的上下文中,代碼才會跑到這裡來,所以不是重點 ... startBatch(); if (shouldCompute(this)) this.value = this.computeValue(false); endBatch(); } else { //給當前Reaction的上下文添加要觀察的對象 reportObserved(this); //判斷是否需要計算 if (shouldCompute(this)) //跟蹤表達式並且計算結果 if (this.trackAndCompute()) propagateChangeConfirmed(this); } ... return result; };function shouldCompute(derivation) { switch (derivation.dependenciesState) { case exports.IDerivationState.UP_TO_DATE: return false; case exports.IDerivationState.NOT_TRACKING: case exports.IDerivationState.STALE: return true; case exports.IDerivationState.POSSIBLY_STALE: { //移除Reaction上下文,因為此處對屬性的訪問純粹為了計算computeValu的值 var prevUntracked = untrackedStart(); var obs = derivation.observing, l = obs.length; for (var i = 0; i < l; i++) { var obj = obs[i]; if (isComputedValue(obj)) { try { //運行computeValue的計算表達式,也就是開發者自己寫的代碼 obj.get(); } catch (e) { // we are not interested in the value *or* exception at this moment, but if there is one, notify all untrackedEnd(prevUntracked); return true; } // if ComputedValue `obj` actually changed it will be computed and propagated to its observers. // and `derivation` is an observer of `obj` if (derivation.dependenciesState === exports.IDerivationState.STALE) { untrackedEnd(prevUntracked); return true; } } } changeDependenciesStateTo0(derivation); //恢復Reaction上下文 untrackedEnd(prevUntracked); return false; } }}ComputedValue.prototype.trackAndCompute = function () { ... var oldValue = this.value; //調用computeValue var newValue = this.value = this.computeValue(true); return isCaughtException(newValue) || valueDidChange(this.compareStructural, newValue, oldValue); };ComputedValue.prototype.computeValue = function (track) { this.isComputing = true; globalState.computationDepth++; var res; if (track) { //從這裡可以看出來其實ComputedValue自身就是一個Reaction, res = trackDerivedFunction(this, this.derivation, this.scope); } else { try { res = this.derivation.call(this.scope); } catch (e) { res = new CaughtException(e); } } //通過以上代碼,ComputeValue便更表達式里訪問的observable建立了觀察的關係 ... return res; };

我們現在通過一個demo來把這個過程走一遍

class Person{ @observable name = 洛丹 changeName(name){ this.name = name; } @compute get fullName(){ return 帥帥的+this.name; }}var p = new Person();autorun(()=>{ console.log(p.fullName)})//代碼1處p.changeName="洛丹1";

運行到代碼1處,通過第一節的分析,autorun的Reaction跟fullName建立觀測的關係,而通過上面的分析,compute的`帥帥的`+this.name表達式跟computeValue的Reaction建立了觀測關係

當我們運行p.changeName的時候,根據前面的知識,會觸發observer的onBecomeStale,不過computeValue的onBecomeStale跟Reaction的onBecomeStale不一樣

onBecomeStale() { //這裡展現了ComputeValue observable的特性 propagateMaybeChanged(this); }export function propagateMaybeChanged(observable: IObservable) { ... while (i--) { const d = observers[i]; if (d.dependenciesState === IDerivationState.UP_TO_DATE) { d.dependenciesState = IDerivationState.POSSIBLY_STALE; //這裡又運行到Reaction的onBecomeStale,而最終的歸屬就是運行Reaction.onInvalidate d.onBecomeStale(); } } // invariantLOS(observable, "maybe end");}Reaction.prototype.runReaction = function () { if (!this.isDisposed) { startBatch(); this._isScheduled = false; //計算Compute值 if (shouldCompute(this)) { this._isTrackPending = true; //運行onInvalidate this.onInvalidate(); if (this._isTrackPending && isSpyEnabled()) { // onInvalidate didnt trigger track right away.. spyReport({ object: this, type: "scheduled-reaction" }); } } endBatch(); } };

Action

compute搞定了,我們來看看稍微簡單點的action,代碼走起

export function createAction(actionName, fn) { invariant(typeof fn === "function", getMessage("m026")); invariant(typeof actionName === "string" && actionName.length > 0, `actions should have valid names, got: ${actionName}`); //把開發者定義的方法用executeAction包了一層 const res = function () { return executeAction(actionName, fn, this, arguments); }; ... return res;}//在運行開發者代碼前運行startAction,運行完開發者代碼後運行endActionexport function executeAction(actionName, fn: Function, scope, args) { const runInfo = startAction(actionName, fn, scope, args); try { return fn.apply(scope, args); } finally { endAction(runInfo); }}function startAction(actionName, fn, scope, args) { ... const prevDerivation = untrackedStart(); //開始批量操作 startBatch(); const prevAllowStateChanges = allowStateChangesStart(true); return { prevDerivation, prevAllowStateChanges, notifySpy, startTime };}function endAction(runInfo: IActionRunInfo) { allowStateChangesEnd(runInfo.prevAllowStateChanges); //提交批量操作 endBatch(); untrackedEnd(runInfo.prevDerivation); if (runInfo.notifySpy) spyReportEnd({ time: Date.now() - runInfo.startTime });}

從源碼可以看出,核心就是通過startBatch和endBatch完成一個批量的操作,也就是說我們在executeAction無論改變多少次observable的值,最終只會觸發一次Reaction的onInvalidate

推薦閱讀:

請教 redux 與 eventEmitter?
揭秘 React 狀態管理
寫在2017的前端數據層不完全指北
redux 源碼研究:中間件

TAG:MobX | React | Redux |