我们将上节用到的几个类的构造器列举一下吧:
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的子类。
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;
};
这样我们就发现三者的共同点。
我们的例子只用到了ObservableValue与autorun,因此先从它们入手。
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是来自其父类BaseAtom
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的实例:
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"
});
}
}
}
};
它是先放到一个全局的列队中执行。