前言
在mobx(5.14.0)源码解读一 observable、mobx(5.14.0)源码解读二 autorun两篇文章中已经将两者关联起来,接下来我们就来介绍一下,在点击"+"按钮,是如何触发autorun回调函数执行的?
开始解读
addBtn.addEventListener('click', ()=> {
vipMember.points ++
})
我们可以看到,"+"按钮点击事件什么都没干,只是简单的做了自增操作,vipMember.points ++,等同于vipMember.points = vipMember.points + 1,对vipMember的points赋值,而vipMember是Proxy的一个实例,所以触发了proxy的handler(即objectProxyTraps)的set拦截器
var objectProxyTraps = {
has: function (target, name) {},
get: function (target, name) {},
set: function (target, name, value) {
if (!isPropertyKey(name))
return false;
set(target, name, value);
return true;
}
}
target就是vipMember,name就是属性key,这里指'points',value就是新设置的值,然后调用set(target, name, value);方法;
function set(obj, key, value) {
if (arguments.length === 2 && !isObservableSet(obj)) {
...
}
if (isObservableObject(obj)) {
var adm = obj[$mobx];
var existingObservable = adm.values.get(key);
if (existingObservable) {
adm.write(key, value);
}
else {
adm.addObservableProp(key, value, adm.defaultEnhancer);
}
}
else if (isObservableMap(obj)) {}
else if(isObservableSet(obj)) {}
...
}
我们是以object为例的,所以只看object相关的逻辑,isObservableObject(obj)判断target是否是object及target.$mobx是否ObservableObjectAdministration类型,前文中$mobx就是通过new ObservableObjectAdministration()创建的,所以这里不展开赘述了,直接看if代码块里的逻辑: 判断在values里是否存在该属性值,存在则调用adm.write(key, value);
ObservableObjectAdministration.prototype.write = function (key, newValue) {
var instance = this.target;
var observable = this.values.get(key);
if (observable instanceof ComputedValue) {
observable.set(newValue);
return;
}
...
newValue = observable.prepareNewValue(newValue);
...
if (newValue !== globalState.UNCHANGED) {
var notify = hasListeners(this);
var notifySpy = isSpyEnabled();
var change = notify || notifySpy
? {
type: "update",
object: this.proxy || instance,
oldValue: observable.value,
name: key,
newValue: newValue
}
: null;
if (notifySpy && process.env.NODE_ENV !== "production")
spyReportStart(__assign({}, change, { name: this.name, key: key }));
observable.setNewValue(newValue);
if (notify)
notifyListeners(this, change);
if (notifySpy && process.env.NODE_ENV !== "production")
spyReportEnd();
}
}
(1)涉及到mobx的另一个方法compute,不在本文讨论范围,不多作介绍
(2)newValue = observable.prepareNewValue(newValue);以及if (newValue !== globalState.UNCHANGED)判断,我们可以将其看成是一个优化手段,如果新老值相同,则不进入到后续的逻辑,我们的demo进行了自增操作,自然新老值是发生变化的,进入if逻辑。
看到一句非常重要的代码observable.setNewValue(newValue);追踪到ObservableValue.prototype.setNewValue方法,看到另外一句this.reportChanged(),报告: 我发生变更了!
ObservableValue.prototype.setNewValue = function (newValue) {
var oldValue = this.value;
this.value = newValue;
this.reportChanged();
...
};
然后调用ObservableValue.reportChanged方法,发现ObservableValue的属性和原型上都不存在该方法,于是从父类中查找,找到Atom的prototype中存在这个方法
Atom.prototype.reportChanged = function () {
startBatch();
propagateChanged(this);
endBatch();
};
function propagateChanged(observable) {
...
observable.observers.forEach(function (d) {
if (d.dependenciesState === IDerivationState.UP_TO_DATE) {
if (d.isTracing !== TraceMode.NONE) {
logTraceInfo(d, observable);
}
d.onBecomeStale();
}
d.dependenciesState = IDerivationState.STALE;
});
}
重点来了,遍历observable.observers(即autorun时收集的reaction),循环调用d.onBecomeStale(); d就是Reaction的一个实例。在Reaction.prototype中找到onBecomeStale方法
Reaction.prototype.onBecomeStale = function () {
this.schedule();
};
嗯?this.schedule?没错,就是在介绍autorun文章中作为切入点的reaction.schedule方法,所以后续是怎么走到调用回调函数的过程就不做介绍的,可以回去复习下mobx(5.14.0)源码解读二 autorun,有一点要提的是,最后也会执行到bindDependencies(derivation)方法,但是这个时候reaction的diffValue是0,不会走到addObserver逻辑。
总结
(1)修改proxy实例的属性值,触发Proxy的handler的set拦截器
(2)根据属性key,匹配到对应的ObservableValue实例, 将新值赋值给原实例,并通知发生变更。
(3)遍历观察者observers(即Reaction实例),执行autorun回调
Mobx 基本原理
(1)observable:负责将对象转换成可观察对象,对该对象进行get、set操作会被拦截。
(2)autorun:通过get拦截器,负责对依赖进行收集,即存储观察者(observer)。
(3)对observable对象进行set操作,触发set拦截器,对observers进行遍历,调用observer对应的行为。