我们将上节用到的几个类的构造器列举一下吧:

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"
});
}
}
}
};

它是先放到一个全局的列队中执行。

mobx源码解读2的更多相关文章

  1. mobx源码解读1

    mobx是redux的代替品,其本身就是一个很好的MVVM框架.因此花点力气研究一下它. 网上下最新的2.75 function Todo() { this.id = Math.random() mo ...

  2. mobx源码解读3

    计算属性 function Todo() { this.id = Math.random() mobx.extendObservable(this, { aaa: 222, bbb: 11, ccc: ...

  3. mobx源码解读4

    这节介绍一下mobx的变动因子的稳定性. mobx整个系统是由ObservableValue, ComputedValue, Reaction这三个东西构建的 ObservableValue 是最小的 ...

  4. SDWebImage源码解读之SDWebImageDownloaderOperation

    第七篇 前言 本篇文章主要讲解下载操作的相关知识,SDWebImageDownloaderOperation的主要任务是把一张图片从服务器下载到内存中.下载数据并不难,如何对下载这一系列的任务进行设计 ...

  5. SDWebImage源码解读 之 NSData+ImageContentType

    第一篇 前言 从今天开始,我将开启一段源码解读的旅途了.在这里先暂时不透露具体解读的源码到底是哪些?因为也可能随着解读的进行会更改计划.但能够肯定的是,这一系列之中肯定会有Swift版本的代码. 说说 ...

  6. SDWebImage源码解读 之 UIImage+GIF

    第二篇 前言 本篇是和GIF相关的一个UIImage的分类.主要提供了三个方法: + (UIImage *)sd_animatedGIFNamed:(NSString *)name ----- 根据名 ...

  7. SDWebImage源码解读 之 SDWebImageCompat

    第三篇 前言 本篇主要解读SDWebImage的配置文件.正如compat的定义,该配置文件主要是兼容Apple的其他设备.也许我们真实的开发平台只有一个,但考虑各个平台的兼容性,对于框架有着很重要的 ...

  8. SDWebImage源码解读_之SDWebImageDecoder

    第四篇 前言 首先,我们要弄明白一个问题? 为什么要对UIImage进行解码呢?难道不能直接使用吗? 其实不解码也是可以使用的,假如说我们通过imageNamed:来加载image,系统默认会在主线程 ...

  9. SDWebImage源码解读之SDWebImageCache(上)

    第五篇 前言 本篇主要讲解图片缓存类的知识,虽然只涉及了图片方面的缓存的设计,但思想同样适用于别的方面的设计.在架构上来说,缓存算是存储设计的一部分.我们把各种不同的存储内容按照功能进行切割后,图片缓 ...

随机推荐

  1. android模拟器停在Waiting for HOME解决方案

    直接打开Android SDK Manager然后再从Android SDK Manager里的tools打开Android AVD Manager,删除掉在Eclipse里创建的模拟器.并在新建一个 ...

  2. day3 字典,集合,文件

    一.深浅copy 浅copy只copy第一层,不copy第二层.copy后,第一层指向不同内存地址.第二层指向相同的内存地址. 导入copy模块,deepcopy深copy.deepcopy后,均指向 ...

  3. [MySQL] 号称永久解决了复制延迟问题的并行复制,MySQL5.7

    一.缘由: 某天看到主从复制延时的告警有点频繁,就想着是不是彻底可以解决一下. 一般主从复制,有三个线程参与,都是单线程:Binlog Dump(主) ----->IO Thread (从) - ...

  4. CentOS 下 sftp 有人能连有人不能连 程序设定了禁用IP

    问题现象: 局域网的一台Linux上运行有sftp Server. 大家用FileZilia 连接,有两个人能够连接,有两个人不能连接. 解决问题: 1.先查看服务器上的sftp server是哪一个 ...

  5. js正则表达式学习

    //几种字符串操作:var str = 'abcdef'; alert(str.search('b')); //弹出1:返回的是b在str中的位置:如果找不到返回-1: alert(str.subst ...

  6. java数据传递例子+内存分析

    一.引用传递 1.例子1 package com.jikexueyuan.ref; class Ref1{ int temp = 10; } public class RefDemo01 { publ ...

  7. [转]理解HTTP幂等性

    基于HTTP协议的Web API是时下最为流行的一种分布式服务提供方式.无论是在大型互联网应用还是企业级架构中,我们都见到了越来越多的SOA或RESTful的Web API.为什么Web API如此流 ...

  8. ndk-stack 使用(分析native代码stack)

    简介: ndk r6 版本之后开始提供该功能. 作用: ndk-stack可以把不认识的内存地址信息转换成可读的信息. 比如,把下列内容 I/DEBUG ( ): *** *** *** *** ** ...

  9. UIPopoverController使用

    *:first-child { margin-top: 0 !important; } body > *:last-child { margin-bottom: 0 !important; } ...

  10. iOS学习系列-Apache服务器的配置

    配置Apache服务器 一.目的 能够有一个测试的服务器,不是所有的特殊网络服务都能找到免费得! 二.为什么我们要用"Apache"? Apache是目前使用最广的web服务器 M ...