Mobx 源码浏览简记

Mobx 源码简记

团体会写得比较乱,同时也比较简朴,和读书笔记差不多,基本是边读边写。包涵~

主要三大部份AtomObservableDerivation

Atom

Mobx的原子类,能够被视察和关照变化,observableValue继续于Atom。observableValue —> Atom

同时内里有几个比较主要的属性与要领。

  • 属性

    • observers,用于寄存这个被原子类被谁视察了,是一个set构造
    • diffValue,后续更新依靠的时刻要用这个来推断
  • 要领

    • reportObserved,挪用全局的reportObserved函数,关照本身被视察了
    • reportChanged,挪用全局的propagateChanged函数,关照本身发作变化了

Observable

Observable是一个工场函数,让数据变得可视察。这个东西须要和上述的Atom竖立联络,行将详细的Atom联络起来。从而买通本身能够被视察,同时能关照变化的全部流程。

三种可被视察的数据范例:对象,数组,Map,下面简朴引见如何完成。假如都不是,就会提醒用户挪用observable.box,使其具有get,set要领,即上述说的observableValue数据范例。

部份代码以下:

fucntion Observable(v) {
    // 假如已被视察,直接返回
    if (isObservable(v)) return v

    // 依据其范例离别挪用observable.object、observable.array、observable.map
    const res = isPlainObject(v)
        ? observable.object(v, arg2, arg3)
        : Array.isArray(v)
            ? observable.array(v, arg2)
            : isES6Map(v)
                ? observable.map(v, arg2)
                : v

    // 返回被视察过的东西
    if (res !== v) return res
    
    // 都不是,提醒用户挪用observable.box(value)
    fail(
        process.env.NODE_ENV !== "production" &&
            `The provided value could not be converted into an observable. If you want just create an observable reference to the object use 'observable.box(value)'`
    )
}

重点是observable.object、observable.array、observable.map三者的完成,下面是议论关于对象的完成体式格局

  • 对象(observable.object)

    • 先建立一个base对象,我们称为adm对象。同时,依据这个base对象建立一个proxy,会经由历程该proxy将会对原对象的种种值举行代办,而adm[$mobx]指向该一个新建的ObservableObjectAdministration数据范例
    • 对传进来的props(即须要被视察的对象),会先寻觅是不是有get属性(即盘算属性),有的话会建立一个盘算属性代办,并和其他的属性一同挂载在该proxy上
    • 有盘算属性时,会新建一个既有observableValue也有derivation属性的computedValue类,寄存到adm[$mobx].values内里,key就是computed的key

      • 同时会拿到它的get函数,作为这个derivation的监听函数,举行初始化监听
      • 并经由历程Object.defineProperty设置了该属性的get和set属性
    • 其他的属性,会新建一个observableValue,寄存到adm[$mobx].values内里,并经由历程Object.defineProperty设置了该属性的get和set属性
    • 然后,重点是建立proxy时的handler对象的getset函数,在有新属性接见时或转变值时会挪用getset函数

      • 接见新属性时,get函数会读取adm[$mobx],假如没有,会经由历程has要领,竖立一个**observableValue**,并放到adm[\$mobx].pendingKeys中(还不晓得有什么用)
      • 设置新属性时,会新建一个observableValue寄存进去adm[$mobx].values中,同时,经由历程Object.defineProperty设置了该属性的get和set属性

重点:

(observableValue简称为oV,Object.defineProperty简称为Od)

  • 上面说的一切经由历程Od定义后的set会挪用已寄存的oVset,get会挪用已寄存的oVget
  • 第一点说过oV继续于Atom,所以oVset会挪用reportChanged,oVget会挪用reportObserved

这模样,全部对象属性的监听流程就竖立起来了

Reaction

Reaction(回响反映)是一类特别的Derivation,能够注册相应函数,使之在前提满足时自动实行。运用以下:

// new Reaction(name, onInvalidate)
const reaction = new Reaction('name', () => {
    // do something,即相应函数,发作副作用的处所
    console.log('excuted!')
})

const ob = observable.object({
    name: 'laji',
    key: '9527'
})

reaction.track(() => {
    // 注册须要被追踪的变量,这里接见了已被视察的ob对象,所以当ob.name或ob.key发作转变时,上面的相应函数会实行
    console.log(`my name is ${ob.name}`)
    console.log(`${ob.key} hey hey hey!`)
})

ob.name = 'mike' // 'excuted!'

让我们剖析一下源码完成,主要有几个重点:

  • 初始化Reaction类时,会将onInvalidate函数存储起来
  • 在挪用track函数时,这个是重点,会挪用trackDerivedFunction(this, fn, undefined)

    • trackDerivedFunction这个函数,就是依靠网络,即注册须要被追踪的变量,它会做几件事变,看下面诠释
    export function trackDerivedFunction<T>(derivation: IDerivation, f: () => T, context: any) {
        // 将该 Derivation 的 dependenciesState 和当前一切依靠的 lowestObserverState 设为最新的状况
        changeDependenciesStateTo0(derivation)
        // 竖立一个该derivation新的newObserving数组,内里寄存的是谁被该derivation注册依靠了
        derivation.newObserving = new Array(derivation.observing.length + 100)
        // 纪录新的依靠的数目
        derivation.unboundDepsCount = 0
        // 每次实行都分派一个全局的 uid
        derivation.runId = ++globalState.runId
        // 重点,将当前的derivation分派为全局的globalState.trackingDerivation,如许被视察的 Observable 在其 reportObserved 要领中就可以获取到该 Derivation
        const prevTracking = globalState.trackingDerivation
        globalState.trackingDerivation = derivation
        let result
        // 下面运转存入track的函数,触发被视察变量的get要领
        if (globalState.disableErrorBoundaries === true) {
            result = f.call(context)
        } else {
            try {
                result = f.call(context)
            } catch (e) {
                result = new CaughtException(e)
            }
        }
        globalState.trackingDerivation = prevTracking
        // 比较新旧依靠,更新依靠
        bindDependencies(derivation)
        return result
    }

能够看到,重点有两个,一个是将当前的derivation分派为全局的globalState.trackingDerivation,一个是下面的更新依靠历程。

  • 接下来,我们看看触发了被视察变量的get要领,会是如何的,上面说过,挪用get要领会实行reportObserved函数

    export function reportObserved(observable: IObservable): boolean {
        // 拿到适才被设置到全局的derivation
        const derivation = globalState.trackingDerivation
        if (derivation !== null) {
            if (derivation.runId !== observable.lastAccessedBy) {
                observable.lastAccessedBy = derivation.runId
                // 这行是重点,将被视察的变量,放到derivation.newObserving数组中,因而,derivation里就寄存了此次接见中被视察的变量
                derivation.newObserving![derivation.unboundDepsCount++] = observable
                if (!observable.isBeingObserved) {
                    observable.isBeingObserved = true
                    observable.onBecomeObserved()
                }
            }
            return true
        } else if (observable.observers.size === 0 && globalState.inBatch > 0) {
            queueForUnobservation(observable)
        }
        return false
    }
  • 以后是bindDependencies函数的实行。这内里有两点,不做代码解读了:

    • 一是主如果比较derivation的新旧observing(寄存被视察变量的数组),防备反复纪录,同时去除已过期的被视察变量
    • 二是,observable(被视察的变量)的observers(是一个Set构造)更新内里寄存的derivation,即纪录本身被谁视察了,在后面挪用reportChanged时,触发相应函数

被视察的变量发作变化时

此时会挪用observable的set函数,然后挪用reportChanged,终究会挪用一个叫做propagateChanged函数。

export function propagateChanged(observable: IObservable) {
    // 已在运转了,直接返回
    if (observable.lowestObserverState === IDerivationState.STALE) return
    observable.lowestObserverState = IDerivationState.STALE

    // 上面说过,observable(被视察的变量)的observers寄存着derivation
    // 这里就是实行每一个derivation的onBecomeStale函数
    observable.observers.forEach(d => {
        if (d.dependenciesState === IDerivationState.UP_TO_DATE) {
            if (d.isTracing !== TraceMode.NONE) {
                logTraceInfo(d, observable)
            }
            d.onBecomeStale()
        }
        d.dependenciesState = IDerivationState.STALE
    })
}

onBecomeStale终究会挪用derivation里的schedule函数,内里做了两件事:

  • 把本身推动全局的globalState.pendingReactions数组
  • 实行runReactions函数

    • 该函数就中心就做一件事变,遍历globalState.pendingReactions数组,实行内里每一个derivation的runReaction函数
    • runReaction终究会挪用derivation本身的onInvalidate,即相应函数

至此,全部mobx的数据视察与相应流程就都逐一诠释完整了(autorun,autorunAsync,when等函数都是基于Reaction来完成的,就不作过多解读了)

Mobx-React源码简记

既然mobx都说了,那就把mobx-react也剖析一下吧。实在很简朴,只需理解了Reaction与Observable,就很轻易邃晓mobx-react的完成了。

mobx-react的完成主要也是两点

  • 经由历程provide和inject,将已被视察过的observerableStore集合起来并按需分派到所须要的组件中
  • 被observer的组件,改写其render函数,使其能够相应变化

第一点比较简朴,完成一个hoc,把observerableStore添加到context上,然后被inject的组件就可以够拿到所需的observerableStore

我们重点看下第二点,完成第二点的主要逻辑,在observer.js内里的makeComponentReactive函数中,看下面简化版的重点剖析

// makeComponentReactive
function makeComponentReactive(render) {
    if (isUsingStaticRendering === true) return render.call(this)
    // 革新后的render函数
    function reactiveRender() {
        // 防备反复实行相应函数,由于componentWillReact有可能有副作用
        isRenderingPending = false
        // render函数实行后返回的jsx
        let rendering = undefined
        // 注册须要被追踪的变量
        reaction.track(() => {
            if (isDevtoolsEnabled) {
                this.__$mobRenderStart = Date.now()
            }
            try {
                // _allowStateChanges是安全地实行本来的render函数,假如在action外有更转变量的行动,会报错
                // 重点是这个,由于render函数被实行了,所以假如内里有被observe过的变量,就可以被追踪,更新到依靠该reaction的依靠列表内里
                rendering = _allowStateChanges(false, baseRender)
            } catch (e) {
                exception = e
            }
            if (isDevtoolsEnabled) {
                this.__$mobRenderEnd = Date.now()
            }
        })
        
        return rendering
    }
    // ....省略一些代码
    // 新建一个Reaction,注册相应函数
    const reaction = new Reaction(`${initialName}#${rootNodeID}.render()`, () => {
        if (!isRenderingPending) {
            // 正在实行相应函数
            isRenderingPending = true
            // 这里就是实行新的componentWillReact生命周期的处所
            if (typeof this.componentWillReact === "function") this.componentWillReact() 
            if (this.__$mobxIsUnmounted !== true) {
                let hasError = true
                try {
                    setHiddenProp(this, isForcingUpdateKey, true)
                    // 也是重点,经由历程forceUpdate,更新组件
                    if (!this[skipRenderKey]) Component.prototype.forceUpdate.call(this)
                    hasError = false
                } finally {
                    setHiddenProp(this, isForcingUpdateKey, false)
                    if (hasError) reaction.dispose()
                }
            }
        }
    })
    // 改写本来的render
    reaction.reactComponent = this
    reactiveRender[mobxAdminProperty] = reaction
    this.render = reactiveRender
    return reactiveRender.call(this)
}

能够见到,经由历程竖立一个Reaction,完成了render函数里的被视察的变量网络及相应函数注册。而且在经由历程forceUpdate从新更新组件后,render函数会被从新实行一遍,从而及时更新被视察的变量。团体的完成照样奇妙的。

除此之外,另有一些生命周期的优化,对props、state也举行监听等操纵,在这里就不逐一解读了

    原文作者:bless_l
    原文地址: https://segmentfault.com/a/1190000018349813
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞