zoukankan      html  css  js  c++  java
  • 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"
                        });
                    }
                }
            }
        };
    

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

  • 相关阅读:
    Redis21:客户端与服务器端的通信与redis管道
    Redis20:keys、scan、bigkeys、查看key的存储方式
    Redis19:限流
    Redis18:分布式锁
    Redis17:cluster集群
    Redis16:两种redis集群解决方案:codis和cluster
    Android : 获取声卡信息的测试代码
    Android : 基于alsa库的音乐播放
    Android system :灯光系统_HAL_lights
    Android system :led_class驱动
  • 原文地址:https://www.cnblogs.com/rubylouvre/p/6058575.html
Copyright © 2011-2022 走看看