mobx(5.14.0)源码解读三 observable属性变更如何触发autorun回调

254 阅读3分钟

前言

mobx(5.14.0)源码解读一 observablemobx(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对应的行为。

其他文章

mobx(5.14.0)源码解读一 observable

mobx(5.14.0)源码解读二 autorun