mobx源码解读2
我们将上节用到的几个类的构造器列举一下吧:
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的更多相关文章
- mobx源码解读1
mobx是redux的代替品,其本身就是一个很好的MVVM框架.因此花点力气研究一下它. 网上下最新的2.75 function Todo() { this.id = Math.random() mo ...
- mobx源码解读3
计算属性 function Todo() { this.id = Math.random() mobx.extendObservable(this, { aaa: 222, bbb: 11, ccc: ...
- mobx源码解读4
这节介绍一下mobx的变动因子的稳定性. mobx整个系统是由ObservableValue, ComputedValue, Reaction这三个东西构建的 ObservableValue 是最小的 ...
- SDWebImage源码解读之SDWebImageDownloaderOperation
第七篇 前言 本篇文章主要讲解下载操作的相关知识,SDWebImageDownloaderOperation的主要任务是把一张图片从服务器下载到内存中.下载数据并不难,如何对下载这一系列的任务进行设计 ...
- SDWebImage源码解读 之 NSData+ImageContentType
第一篇 前言 从今天开始,我将开启一段源码解读的旅途了.在这里先暂时不透露具体解读的源码到底是哪些?因为也可能随着解读的进行会更改计划.但能够肯定的是,这一系列之中肯定会有Swift版本的代码. 说说 ...
- SDWebImage源码解读 之 UIImage+GIF
第二篇 前言 本篇是和GIF相关的一个UIImage的分类.主要提供了三个方法: + (UIImage *)sd_animatedGIFNamed:(NSString *)name ----- 根据名 ...
- SDWebImage源码解读 之 SDWebImageCompat
第三篇 前言 本篇主要解读SDWebImage的配置文件.正如compat的定义,该配置文件主要是兼容Apple的其他设备.也许我们真实的开发平台只有一个,但考虑各个平台的兼容性,对于框架有着很重要的 ...
- SDWebImage源码解读_之SDWebImageDecoder
第四篇 前言 首先,我们要弄明白一个问题? 为什么要对UIImage进行解码呢?难道不能直接使用吗? 其实不解码也是可以使用的,假如说我们通过imageNamed:来加载image,系统默认会在主线程 ...
- SDWebImage源码解读之SDWebImageCache(上)
第五篇 前言 本篇主要讲解图片缓存类的知识,虽然只涉及了图片方面的缓存的设计,但思想同样适用于别的方面的设计.在架构上来说,缓存算是存储设计的一部分.我们把各种不同的存储内容按照功能进行切割后,图片缓 ...
随机推荐
- angular2开发01
// */ // ]]> angular2开发01 1. angular2 开发准备 1.1. 安装node 1.2. 安装npm 1.3. 运行qickStart 1 angular2 开发准 ...
- View加载过程
1. 先判断子类是否重写了loadView,如果有直接调用.之后调viewDidLoad完成View的加载.2 .如果是外部通过调用initWithNibName:bundle指定nib文件名的话,V ...
- HotSpot 自动内存管理笔记与实战
1.对象的创建 虚拟机遇到一条new指令时,首先会去检查这个指令的参数是否能在常量池中定位到一个类的符号引用,并且检查这个符号引用代表的类是否已被加载.解析和初始化过.如果没有,则必须先进行相应的类的 ...
- 转,SelectNodes + XPath
XPath 是 XML 的内容,这里 SelectNodes 是 C# 中 XmlDocument 或 XmlNode 的一个方法.SelectNodes 使用 XPath 来选取节点. 重要语法 S ...
- 缓存AsimpleCache -- 解决Android中Sharedpreferences无法存储List数据/ASimpleCache
Sharedpreferences想必大家在项目中都经常会用到,但是如果需要在本地需要存储比较多的数据,存储一个集合的时,发现Sharedpreferences并不 是那么好使了. 分析 如果需要在本 ...
- HBase内部操作日志说明
版本:0.94-cdh4.2.1 1. Split Region [regionserver60020-splits-1397585864985] INFO org.apache.hadoop.hba ...
- CentOS下安装LAMP环境
1.安装Apache yum -y install httpd # 开机自启动 chkconfig httpd on # 启动httpd 服务 service httpd start #安装apach ...
- Git——1
集中式版本控制系统,版本库是集中存放在中央服务器的,而干活的时候,用的都是自己的电脑,所以要先从中央服务器取得最新的版本,然后开始干活,干完活了,再把自己的活推送给中央服务器.中央服务器就好比是一个图 ...
- ios开发中遇到的编译错误总结
1:Undefined symbols for architecture arm64: ? 1 2 3 <code>Undefined symbols for architecture a ...
- UIViewContentMode各类型效果
UIViewContentMode typedef enum { UIViewContentModeScaleToFill, UIViewContentModeScaleAspectF ...