咱們將上節用到的幾個類的構造器列舉一下吧:vue
function Reaction(name, onInvalidate) { if (name === void 0) { name = "Reaction@" + getNextId(); } this.name = name; this.onInvalidate = onInvalidate; this.observing = []; this.newObserving = []; this.dependenciesState = IDerivationState.NOT_TRACKING; this.diffValue = 0; this.runId = 0; this.unboundDepsCount = 0; this.__mapid = "#" + getNextId(); this.isDisposed = false; this._isScheduled = false; this._isTrackPending = false; this._isRunning = false; } function ComputedValue(derivation, scope, compareStructural, name, setter) { this.derivation = derivation; this.scope = scope; this.compareStructural = compareStructural; this.dependenciesState = IDerivationState.NOT_TRACKING; this.observing = []; this.newObserving = null; this.isPendingUnobservation = false; this.observers = []; this.observersIndexes = {}; this.diffValue = 0; this.runId = 0; this.lastAccessedBy = 0; this.lowestObserverState = IDerivationState.UP_TO_DATE; this.unboundDepsCount = 0; this.__mapid = "#" + getNextId(); this.value = undefined; this.isComputing = false; this.isRunningSetter = false; this.name = name || "ComputedValue@" + getNextId(); if (setter) this.setter = createAction(name + "-setter", setter); } function ObservableValue(value, mode, name, notifySpy) { if (name === void 0) { name = "ObservableValue@" + getNextId(); } if (notifySpy === void 0) { notifySpy = true; } _super.call(this, name); this.mode = mode; this.hasUnreportedChange = false; this.value = undefined; var _a = getValueModeFromValue(value, ValueMode.Recursive), childmode = _a[0], unwrappedValue = _a[1]; if (this.mode === ValueMode.Recursive) this.mode = childmode; this.value = makeChildObservable(unwrappedValue, this.mode, this.name); if (notifySpy && isSpyEnabled()) { spyReport({ type: "create", object: this, newValue: this.value }); } }
乍一看,ObservableValue與其餘兩個出入巨大,但它是BaseAtom的子類。node
function BaseAtom(name) { if (name === void 0) { name = "Atom@" + getNextId(); } this.name = name; this.isPendingUnobservation = true; this.observers = []; this.observersIndexes = {}; this.diffValue = 0; this.lastAccessedBy = 0; this.lowestObserverState = IDerivationState.NOT_TRACKING; } BaseAtom.prototype.onBecomeUnobserved = function () { }; BaseAtom.prototype.reportObserved = function () { reportObserved(this); }; BaseAtom.prototype.reportChanged = function () { transactionStart("propagatingAtomChange", null, false); propagateChanged(this); transactionEnd(false); }; BaseAtom.prototype.toString = function () { return this.name; };
這樣咱們就發現三者的共同點。react
咱們的例子只用到了ObservableValue與autorun,所以先從它們入手。api
function Todo() { this.id = Math.random() mobx.extendObservable(this, { aaa: 111, bbb: 222 }) } var vm = new Todo mobx.autorun(function () { console.log(vm.aaa + " " + vm.bbb) })
當autorun的回調被執行時,會獲取vm.aaa, vm.bbb 的值,這會跑到下面的代碼裏數組
get: function () { return this.$mobx.values[propName].get(); },
至關於observable.get()
,咱們看一下它的實現
ObservableValue.prototype.get = function () { this.reportObserved(); return this.value; };
而這裏面的reportObserved是來自其父類BaseAtomapp
BaseAtom.prototype.reportObserved = function () { reportObserved(this); };
這時咱們終於看到一些咱們熟悉的東西了,這不是就是依賴收集嗎,將本身暴露出去,讓須要的視圖刷新函數或計算屬性來收集它。固然,這話須要你看過avalon, knockout才行。每一個庫 的實現也不同,咱們看一下它有什麼精妙之處:框架
function reportObserved(observable) { var derivation = globalState.trackingDerivation; if (derivation !== null) { if (derivation.runId !== observable.lastAccessedBy) { observable.lastAccessedBy = derivation.runId; derivation.newObserving[derivation.unboundDepsCount++] = observable; } } else if (observable.observers.length === 0) { queueForUnobservation(observable); } }
裏面有一個globalState,它是MobXGlobals的實例:dom
function MobXGlobals() { this.version = 4; this.trackingDerivation = null; this.runId = 0; this.mobxGuid = 0; this.inTransaction = 0; this.isRunningReactions = false; this.inBatch = 0; this.pendingUnobservations = []; this.pendingReactions = []; this.allowStateChanges = true; this.strictMode = false; this.resetId = 0; this.spyListeners = []; }
若是咱們在reportObserved 方法里加入一個console.log函數
function reportObserved(observable) { var derivation = globalState.trackingDerivation; console.log(derivation,observable) //.... }
運行代碼,發現trackingDerivation其實就是autorun生成的Reaction實例性能
它將監控屬性先放進newObserving數組中,而後又挪進observing數組裏
再看監控屬性,它的lastAccessedBy變成Reaction 的runid, 裏面的observe則存放着 reaction,這樣雙方就互相存放着對象的引用。你能夠呼叫我,我也能夠呼叫你,這就是雙向綁定。
在這過程當中,vm中的屬性是被動來賣身
,autotun是主動進行購賣。 Reaction.prototype.track是一個很重要的方法,它是用來追蹤依賴,knockout裏也有這提法。若是將裏面的spy代碼去掉,它是長成這樣:
Reaction.prototype.track = function (fn) { startBatch(); var startTime; this._isRunning = true; trackDerivedFunction(this, fn); this._isRunning = false; this._isTrackPending = false; if (this.isDisposed) { clearObserving(this); } endBatch(); };
有時我以爲mobx在最開始時性能是否是不好,每一步都要通過這麼多方法。startBatch,endBatch先略過,看trackDerivedFunction方法
function trackDerivedFunction(derivation, f) { changeDependenciesStateTo0(derivation); derivation.newObserving = new Array(derivation.observing.length + 100); derivation.unboundDepsCount = 0; derivation.runId = ++globalState.runId; var prevTracking = globalState.trackingDerivation; globalState.trackingDerivation = derivation; var hasException = true; var result; try { result = f.call(derivation); hasException = false; } finally { if (hasException) { handleExceptionInDerivation(derivation); } else { globalState.trackingDerivation = prevTracking; bindDependencies(derivation); } } return result; }
這裏就是重寫derivation的許多屬性,而後將本身放到globalState上,f則會調用vm的屬性。
bindDependencies方法也很重要,它是將newObserving變成observing,一些失效的監控屬性會被去掉,新的加進去。這能夠解決用戶方法裏面存在if語句,每次收集的依賴不一的狀況。
function bindDependencies(derivation) { var prevObserving = derivation.observing; var observing = derivation.observing = derivation.newObserving; derivation.newObserving = null; var i0 = 0, l = derivation.unboundDepsCount; for (var i = 0; i < l; i++) { var dep = observing[i]; if (dep.diffValue === 0) { dep.diffValue = 1; if (i0 !== i) observing[i0] = dep; i0++; } } observing.length = i0; l = prevObserving.length; while (l--) { var dep = prevObserving[l]; if (dep.diffValue === 0) { removeObserver(dep, derivation); } dep.diffValue = 0; } while (i0--) { var dep = observing[i0]; if (dep.diffValue === 1) { dep.diffValue = 0; addObserver(dep, derivation); } } } function addObserver(observable, node) { var l = observable.observers.length; if (l) { observable.observersIndexes[node.__mapid] = l; } observable.observers[l] = node; if (observable.lowestObserverState > node.dependenciesState) observable.lowestObserverState = node.dependenciesState; } function removeObserver(observable, node) { if (observable.observers.length === 1) { observable.observers.length = 0; queueForUnobservation(observable); } else { var list = observable.observers; var map_1 = observable.observersIndexes; var filler = list.pop(); if (filler !== node) { var index = map_1[node.__mapid] || 0; if (index) { map_1[filler.__mapid] = index; } else { delete map_1[filler.__mapid]; } list[index] = filler; } delete map_1[node.__mapid]; } } function queueForUnobservation(observable) { if (!observable.isPendingUnobservation) { observable.isPendingUnobservation = true; globalState.pendingUnobservations.push(observable); } } function startBatch() { globalState.inBatch++; } function endBatch() { if (globalState.inBatch === 1) { var list = globalState.pendingUnobservations; for (var i = 0; i < list.length; i++) { var observable_1 = list[i]; observable_1.isPendingUnobservation = false; if (observable_1.observers.length === 0) { observable_1.onBecomeUnobserved(); } } globalState.pendingUnobservations = []; } globalState.inBatch--; }
它的依賴收集很是強大,不像vue,須要開延時,也能實現批處理。批處理的目的是,將一大堆監聽屬性放到一個數組,而後去重,從而減小要處理的監聽屬性的註冊或觸發工做。當一個vm存在複雜的子對象時,這種機制就很是有用。
mobx與react也中發展出一種不用try, catch就能斷定是否出錯的技術,那就是 try finally,那個中間生成的異常對象對框架沒用就乾脆不生成了。
咱們再來看 Reaction.prototype.schedule,它是用來執行autorun的那個方法的
Reaction.prototype.schedule = function () { if (!this._isScheduled) { this._isScheduled = true; globalState.pendingReactions.push(this); startBatch(); runReactions(); endBatch(); } };
function runReactions() { if (globalState.isRunningReactions === true || globalState.inTransaction > 0) return; globalState.isRunningReactions = true; var allReactions = globalState.pendingReactions; var iterations = 0; while (allReactions.length > 0) { if (++iterations === MAX_REACTION_ITERATIONS) { resetGlobalState(); throw new Error(("Reaction doesn't converge to a stable state after " + MAX_REACTION_ITERATIONS + " iterations.") + (" Probably there is a cycle in the reactive function: " + allReactions[0])); } var remainingReactions = allReactions.splice(0); for (var i = 0, l = remainingReactions.length; i < l; i++) remainingReactions[i].runReaction(); } globalState.isRunningReactions = false; }
Reaction.prototype.runReaction = function () { if (!this.isDisposed) { this._isScheduled = false; if (shouldCompute(this)) { this._isTrackPending = true; this.onInvalidate(); if (this._isTrackPending && isSpyEnabled()) { spyReport({ object: this, type: "scheduled-reaction" }); } } } };
它是先放到一個全局的列隊中執行。