阅读 274

mobx源码解读-autorun(mobx中文文档)

第一次阅读源码,可能有理解的不太正确的地方希望大佬们能帮我纠正。开始看的是6后来看到observable发现和5的差距还是有一点的,所以在所以《mobx源码解读-autorun》里边可能会有6的源码,但差距不大。

1.mobx的基本概念

Observable 被观察者

Observer 观察

Reaction 响应

var student = mobx.observable({ name: '张三', }); mobx.autorun(() => { console.log('张三的名字:', name); });

2.mobx的原理

1.在响应式函数中(如以上autorun中通常会访问一个或多个observable对象)

  • 1)autorun首先创建一个Reaction类型的实例对象reaction,通过参数track一个响应式函数的回调函数。

  • 2)然后执行reaction.schedule_方法,执行回调函数,回调函数中调用被观察者observable.get方法,触发reportObserved方法。

  • 3)reportObserved方法中会将observavle对象收集到globalState.trackingDerivation.newObserving_队列中(globalState.trackingDerivation此时等同于reaction对象)

  • 4)处理reaction和observable的依赖关系,遍历reaction.newObserving_属性,在newObserving_队列中的每一个observable.observers_属性中添加当前reaction对象。

2.被观察者observable的value发生变化,调用observable对象set方法,触发propagateChange方法。propagateChange方法中,遍历observable.observers_属性依次执行reaction.onBecomeStale方法,再次将以上的2)3)4)执行一遍。

--------------------------------------------------------------------------------------------

3.源码解读(以下为删减后的代码)

export function autorun(
    view: (r: IReactionPublic) => any,// autoruan函数的回调函数
    opts: IAutorunOptions = EMPTY_OBJECT
): IReactionDisposer {
    
    const name: string = "Autorun"
    const runSync = !opts.scheduler && !opts.delay
    
    // 首先创建一个Reaction类型的对象 主要功能是用来控制任务的执行
    let reaction = new Reaction(
        name,
        function (this: Reaction) {
            this.track(reactionRunner)
        },
        opts.onError,
        opts.requiresObservable
    )

    function reactionRunner() { view(reaction) } // view即autorun函数的回调

    reaction.schedule() // 立即执行一次部署    
    
    return reaction.getDisposer() // 用于在执行期间清理 autorun
}复制代码

从上边的源码可以看出autorun主要做了一下三个动作

1)创建一个Reaction类型的对象 主要功能是用来控制任务的执行

2)将view即auto的回调函数,分配给reaction.track

3)立即执行一次部署 ,此时你应该理解文档中所说的“当使用autorun时,所提供的函数总是立即被触发”

接下来看reaction.schedule的源码

schedule() {
    if (!this.isScheduled) {
        this.isScheduled = true
        globalState.pendingReactions.push(this) // 当前的reaction对象入列
        runReactions() // 队列中的所有reaction对象执行runReaction_方法
    }
}

function runReactionsHelper() {
   let remainingReactions = allReactions.splice(0)
   for (let i = 0, l = remainingReactions.length; i < l; i++)
        remainingReactions[i].runReaction()
}复制代码

schedule方法做了两件事

1)当前的reaction对象入列

  1. 队列中的所有reaction对象执行runReaction_方法

runReaction源码

runReaction() {
    startBatch() // 开启一层事务
    this.isScheduled = false
   
    if (shouldCompute(this)) {// derivation.dependenciesState默认为-1 (未跟踪) 
       this.onInvalidate()
    }
    endBatch() // 关闭一层事务 startBatch和endBatch总是成对出现
}复制代码

翻看上边的代码可以发现onInvalidate_是在初始化Reaction时传入构造函数的,实际上时调用了reaction.track方法

track(fn: () => void) {
    startBatch()
    ...
    const result = trackDerivedFunction(this, fn, undefined) // 执行任务 更新依赖
    ...
    endBatch()
}复制代码

track方法主要是调用了trackDerivedFunction

export function trackDerivedFunction<T>(derivation: IDerivation, f: () => T, context: any) {
    ...
    globalState.trackingDerivation = derivation  // 将derivation(此处等同于reaction对象)挂载到全局变量 这样其他成员也可访问此derivation
    ...
    // 执行reaction传递的的回调方法,翻看代码可以看出执行的是autoran函数的回调方法
    // 回调中一般会调用一或多个observable对象,触发observable.get方法,再触发reportObserved方法
    let result = f.call(context)
    ...
    globalState.trackingDerivation = prevTracking
    bindDependencies(derivation) // 更新observable和raction的依赖关系
    return result
}复制代码

执行因为autorun回调用到了student.name变量,这里的"."其实就是get操作;一旦涉及到get操作,监督这个name的属性的观察员就会执行reportObserved方法(后边介绍Oobservable时候会重点介绍这里)。来看一下reportObserved源码

export function reportObserved(observable: IObservable): boolean {
    ...
    const derivation = globalState.trackingDerivation
    if (derivation !== null) {

        if (derivation.runId_ !== observable.lastAccessedBy_) {
            
             // 更被观察者的lastAccessedBy_属性(事务id),这个是为了避免重复操作
            observable.lastAccessedBy_ = derivation.runId_ 
            
            // 更新derivation(此处为reaction)的newObserving属性,将被观察者加入该队列中
            // 后续derivation和observable更新依赖关系就靠这个属性
            derivation.newObserving_![derivation.unboundDepsCount_++] = observable 

            ...
        }
        return true
    } else if (observable.observers_.size === 0 && globalState.inBatch > 0) {
        queueForUnobservation(observable)
    }

    return false
}复制代码

上边的代码,我们主要关注影响derivation的操作

1)更新observable的lastAccessedBy_属性(事务id),这个是为了避免重复操作。

2)更新derivation(此处为reaction)的newObserving属性,将observable加入该队列中,后续derivation和observable更新依赖关系就靠这个属性

随后autorun的任务执行完成后,derivation就开始着手更新和被观察者observable的依赖关系

bindDependencies源码

function bindDependencies(derivation: IDerivation) {
    const prevObserving = derivation.observing_
     
    // derivation.newObserving_为derivation依赖的observable对象的队列
    const observing = (derivation.observing_ = derivation.newObserving_!)
    let lowestNewObservingDerivationState = IDerivationState_.UP_TO_DATE_ // 默认为0

    let i0 = 0,
        l = derivation.unboundDepsCount_
    for (let i = 0; i < l; i++) {

        /**
         * 以下是一个去重的过程 
         * observable.diffValue_默认是0
         * 循环时候置为1,因为observing为Observable类型的对象数组,所以不同位置上相同的值的diffValue_都会变成1
         * 在遍历到重复项后就不会进入下边的判断,i0就不会++
         * 遍历到非重复项(diffValue_为0的项),则直接将此项填充到i0对应的位置上
         * 这样数组循环完毕,i0即非重复项的数量,observing.length = i0即删除掉了多余项
         */
        
        const dep = observing[i]
        if (dep.diffValue_ === 0) {
            dep.diffValue_ = 1
            if (i0 !== i) observing[i0] = dep
            i0++
        }
    }
    observing.length = i0

    derivation.newObserving_ = null // newObserving 置空

    /**
     * prevObserving中和observing中存在的均为observable对象 
     * 此时如果在上边的循环完成后 observing存在的observable对象的diffValue_均为1
     * 在prevObserving队列如果是diffValue_仍然为0,表示当前derivation已经不依赖此observable对象
     */
    l = prevObserving.length
    while (l--) {
        const dep = prevObserving[l];
        if (dep.diffValue_ === 0) {
            // 将当前derivation已不再依赖此observable对象,将其从observable.observers_中的deleted掉
            removeObserver(dep, derivation)  
        }
        dep.diffValue_ = 0 // 将prevObserving队列中的observable的diffValue_均置为0
    }

    
    while (i0--) {
        const dep = observing[i0]
        
        // observing仍然为1的说明此observable对象不在prevObserving队列中 
        if (dep.diffValue_ === 1) {
            dep.diffValue_ = 0
            // 在observable.observers_中添加当前的derivation对象
            addObserver(dep, derivation) 
        }
    }
    // 通过以上的3次循环,将derivation.observing更新为最新的依赖(并去重),
    // 并在已经不依赖的observable对象的observers_中delete当前的derivation对象
    // 在新建立起的依赖的observable对象的observers_中add当前的derivation对象
}复制代码

响应被观察者observable对象的value发生变化

上边提及,一旦observable的value发生变化,就会触发observable.get方法,然后触发propagateChange方法,propageateChange源码如下

export function propagateChanged(observable: IObservable) {
    ...
    observable.observers_.forEach(d => {
        ...
            d.onBecomeStale_()
        ...
    })
    
}
observable.observers_存储的是,与observable对象有依赖关系的derivation对象,在propagateChanged方法中,遍历observers_执行derivation对象的onBecomeStale_方法,我们来看一下onBecomeStale_的源码onBecomeStale_() {
    this.schedule_()
}复制代码

this.schedule_是不是很熟悉,翻一下上边的代码,发现是在autorun函数中创建reaction对像的时候调用了reaction.schedule_()。所以这下明白propagateChanged调用onBecomeStale_是让reaction再次执行一次之前的部署操作(也就是执行autorun的回调,处理依赖关系);


作者:Jodie同志
链接:https://juejin.cn/post/7037039973157044254

 伪原创工具 SEO网站优化  https://www.237it.com/ 


文章分类
代码人生
版权声明:本站是系统测试站点,无实际运营。本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 XXXXXXo@163.com 举报,一经查实,本站将立刻删除。
相关推荐