Bless-L / MyBlog

时不时会写一些文章
23 stars 2 forks source link

Mobx 源码阅读简记 #12

Open Bless-L opened 5 years ago

Bless-L commented 5 years ago

Mobx 源码简记

整体会写得比较乱,同时也比较简单,和读书笔记差不多,基本是边读边写。见谅~

主要三大部分AtomObservableDerivation

Atom

Mobx的原子类,能够被观察和通知变化,observableValue继承于Atom。observableValue ---> Atom

同时里面有几个比较重要的属性与方法。

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三者的实现,下面是讨论关于对象的实现方式

重点:

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

这样子,整个对象属性的监听流程就建立起来了

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!'

让我们分析一下源码实现,主要有几个重点:

被观察的变量发生变化时

此时会调用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函数,里面做了两件事:

至此,整个mobx的数据观察与响应流程就都一一解释完整了(autorun,autorunAsync,when等函数都是基于Reaction来实现的,就不作过多解读了)

Mobx-React源码简记

既然mobx都说了,那就把mobx-react也分析一下吧。其实很简单,只要理解了Reaction与Observable,就很容易明白mobx-react的实现了。

mobx-react的实现主要也是两点

第一点比较简单,实现一个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也进行监听等操作,在这里就不一一解读了