成人国产在线小视频_日韩寡妇人妻调教在线播放_色成人www永久在线观看_2018国产精品久久_亚洲欧美高清在线30p_亚洲少妇综合一区_黄色在线播放国产_亚洲另类技巧小说校园_国产主播xx日韩_a级毛片在线免费

資訊專欄INFORMATION COLUMN

Mobx 源碼閱讀簡記

paulli3 / 2781人閱讀

摘要:源碼簡記整體會寫得比較亂,同時也比較簡單,和讀書筆記差不多,基本是邊讀邊寫。見諒主要三大部分的原子類,能夠被觀察和通知變化,繼承于。同時里面有幾個比較重要的屬性與方法。

Mobx 源碼簡記

整體會寫得比較亂,同時也比較簡單,和讀書筆記差不多,基本是邊讀邊寫。見諒~

主要三大部分Atom、ObservableDerivation

Atom

Mobx的原子類,能夠被觀察和通知變化,observableValue繼承于Atom。observableValue ---> Atom

同時里面有幾個比較重要的屬性與方法。

屬性

observers,用于存放這個被原子類被誰觀察了,是一個set結(jié)構(gòu)

diffValue,后續(xù)更新依賴的時候要用這個來判斷

方法

reportObserved,調(diào)用全局的reportObserved函數(shù),通知自身被觀察了

reportChanged,調(diào)用全局的propagateChanged函數(shù),通知自身發(fā)生變化了

Observable

Observable是一個工廠函數(shù),讓數(shù)據(jù)變得可觀察。這個東西需要和上述的Atom建立聯(lián)系,即將具體的Atom聯(lián)系起來。從而打通自身能夠被觀察,同時能通知變化的整個流程。

三種可被觀察的數(shù)據(jù)類型:對象,數(shù)組,Map,下面簡單介紹如何實現(xiàn)。假如都不是,就會提示用戶調(diào)用observable.box,使其擁有g(shù)et,set方法,即上述說的observableValue數(shù)據(jù)類型。

部分代碼如下:

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

    // 根據(jù)其類型分別調(diào)用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
    
    // 都不是,提示用戶調(diào)用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三者的實現(xiàn),下面是討論關(guān)于對象的實現(xiàn)方式

對象(observable.object)

先創(chuàng)建一個base對象,我們稱為adm對象。同時,根據(jù)這個base對象創(chuàng)建一個proxy,會通過該proxy將會對原對象的各種值進行代理,而adm[$mobx]指向該一個新建的ObservableObjectAdministration數(shù)據(jù)類型

對傳進來的props(即需要被觀察的對象),會先尋找是否有g(shù)et屬性(即計算屬性),有的話會創(chuàng)建一個計算屬性代理,并和其余的屬性一起掛載在該proxy上

有計算屬性時,會新建一個既有observableValue也有derivation屬性的computedValue類,存放到adm[$mobx].values里面,key就是computed的key

同時會拿到它的get函數(shù),作為這個derivation的監(jiān)聽函數(shù),進行初始化監(jiān)聽

并通過Object.defineProperty設置了該屬性的get和set屬性

其余的屬性,會新建一個observableValue,存放到adm[$mobx].values里面,并通過Object.defineProperty設置了該屬性的get和set屬性

然后,重點是創(chuàng)建proxy時的handler對象的getset函數(shù),在有新屬性訪問時或改變值時會調(diào)用getset函數(shù)

訪問新屬性時,get函數(shù)會讀取adm[$mobx],如果沒有,會通過has方法,建立一個**observableValue**,并放到adm[$mobx].pendingKeys中(還不知道有什么用)

設置新屬性時,會新建一個observableValue存放進去adm[$mobx].values中,同時,通過Object.defineProperty設置了該屬性的get和set屬性

重點:

(observableValue簡稱為oV,Object.defineProperty簡稱為Od)

上面說的所有通過Od定義后的set會調(diào)用已存放的oVset,get會調(diào)用已存放的oVget

第一點說過oV繼承于Atom,所以oVset會調(diào)用reportChanged,oVget會調(diào)用reportObserved

這樣子,整個對象屬性的監(jiān)聽流程就建立起來了

Reaction

Reaction(反應)是一類特殊的Derivation,可以注冊響應函數(shù),使之在條件滿足時自動執(zhí)行。使用如下:

// new Reaction(name, onInvalidate)
const reaction = new Reaction("name", () => {
    // do something,即響應函數(shù),發(fā)生副作用的地方
    console.log("excuted!")
})

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

reaction.track(() => {
    // 注冊需要被追蹤的變量,這里訪問了已經(jīng)被觀察的ob對象,所以當ob.name或ob.key發(fā)生改變時,上面的響應函數(shù)會執(zhí)行
    console.log(`my name is ${ob.name}`)
    console.log(`${ob.key} hey hey hey!`)
})

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

讓我們分析一下源碼實現(xiàn),主要有幾個重點:

初始化Reaction類時,會將onInvalidate函數(shù)存儲起來

在調(diào)用track函數(shù)時,這個是重點,會調(diào)用trackDerivedFunction(this, fn, undefined)

trackDerivedFunction這個函數(shù),就是依賴收集,即注冊需要被追蹤的變量,它會做幾件事情,看下面注釋

export function trackDerivedFunction(derivation: IDerivation, f: () => T, context: any) {
    // 將該 Derivation 的 dependenciesState 和當前所有依賴的 lowestObserverState 設為最新的狀態(tài)
    changeDependenciesStateTo0(derivation)
    // 建立一個該derivation新的newObserving數(shù)組,里面存放的是誰被該derivation注冊依賴了
    derivation.newObserving = new Array(derivation.observing.length + 100)
    // 記錄新的依賴的數(shù)量
    derivation.unboundDepsCount = 0
    // 每次執(zhí)行都分配一個全局的 uid
    derivation.runId = ++globalState.runId
    // 重點,將當前的derivation分配為全局的globalState.trackingDerivation,這樣被觀察的 Observable 在其 reportObserved 方法中就能獲取到該 Derivation
    const prevTracking = globalState.trackingDerivation
    globalState.trackingDerivation = derivation
    let result
    // 下面運行存入track的函數(shù),觸發(fā)被觀察變量的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,一個是下面的更新依賴過程。

接下來,我們看看觸發(fā)了被觀察變量的get方法,會是怎樣的,上面說過,調(diào)用get方法會執(zhí)行reportObserved函數(shù)

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數(shù)組中,因此,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函數(shù)的執(zhí)行。這里面有兩點,不做代碼解讀了:

一是主要是比較derivation的新舊observing(存放被觀察變量的數(shù)組),防止重復記錄,同時去除已過期的被觀察變量

二是,observable(被觀察的變量)的observers(是一個Set結(jié)構(gòu))更新里面存放的derivation,即記錄自身被誰觀察了,在后面調(diào)用reportChanged時,觸發(fā)響應函數(shù)

被觀察的變量發(fā)生變化時

此時會調(diào)用observable的set函數(shù),然后調(diào)用reportChanged,最終會調(diào)用一個叫做propagateChanged函數(shù)。

export function propagateChanged(observable: IObservable) {
    // 已經(jīng)在運行了,直接返回
    if (observable.lowestObserverState === IDerivationState.STALE) return
    observable.lowestObserverState = IDerivationState.STALE

    // 上面說過,observable(被觀察的變量)的observers存放著derivation
    // 這里就是執(zhí)行每個derivation的onBecomeStale函數(shù)
    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最終會調(diào)用derivation里的schedule函數(shù),里面做了兩件事:

把自身推進全局的globalState.pendingReactions數(shù)組

執(zhí)行runReactions函數(shù)

該函數(shù)就核心就做一件事情,遍歷globalState.pendingReactions數(shù)組,執(zhí)行里面每個derivation的runReaction函數(shù)

runReaction最終會調(diào)用derivation自身的onInvalidate,即響應函數(shù)

至此,整個mobx的數(shù)據(jù)觀察與響應流程就都一一解釋完整了(autorun,autorunAsync,when等函數(shù)都是基于Reaction來實現(xiàn)的,就不作過多解讀了)

Mobx-React源碼簡記

既然mobx都說了,那就把mobx-react也分析一下吧。其實很簡單,只要理解了Reaction與Observable,就很容易明白mobx-react的實現(xiàn)了。

mobx-react的實現(xiàn)主要也是兩點

通過provide和inject,將已經(jīng)被觀察過的observerableStore集中起來并按需分配到所需要的組件中

被observer的組件,改寫其render函數(shù),使其可以響應變化

第一點比較簡單,實現(xiàn)一個hoc,把observerableStore添加到context上,然后被inject的組件就可以拿到所需的observerableStore

我們重點看下第二點,實現(xiàn)第二點的主要邏輯,在observer.js里面的makeComponentReactive函數(shù)中,看下面簡化版的重點解析

// makeComponentReactive
function makeComponentReactive(render) {
    if (isUsingStaticRendering === true) return render.call(this)
    // 改造后的render函數(shù)
    function reactiveRender() {
        // 防止重復執(zhí)行響應函數(shù),因為componentWillReact有可能有副作用
        isRenderingPending = false
        // render函數(shù)執(zhí)行后返回的jsx
        let rendering = undefined
        // 注冊需要被追蹤的變量
        reaction.track(() => {
            if (isDevtoolsEnabled) {
                this.__$mobRenderStart = Date.now()
            }
            try {
                // _allowStateChanges是安全地執(zhí)行原來的render函數(shù),假如在action外有更改變量的行為,會報錯
                // 重點是這個,因為render函數(shù)被執(zhí)行了,所以假如里面有被observe過的變量,就能被追蹤,更新到依賴該reaction的依賴列表里面
                rendering = _allowStateChanges(false, baseRender)
            } catch (e) {
                exception = e
            }
            if (isDevtoolsEnabled) {
                this.__$mobRenderEnd = Date.now()
            }
        })
        
        return rendering
    }
    // ....省略一些代碼
    // 新建一個Reaction,注冊響應函數(shù)
    const reaction = new Reaction(`${initialName}#${rootNodeID}.render()`, () => {
        if (!isRenderingPending) {
            // 正在執(zhí)行響應函數(shù)
            isRenderingPending = true
            // 這里就是執(zhí)行新的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,實現(xiàn)了render函數(shù)里的被觀察的變量收集及響應函數(shù)注冊。而且在通過forceUpdate重新更新組件后,render函數(shù)會被重新執(zhí)行一遍,從而實時更新被觀察的變量。整體的實現(xiàn)還是巧妙的。

除此之外,還有一些生命周期的優(yōu)化,對props、state也進行監(jiān)聽等操作,在這里就不一一解讀了

文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請注明本文地址:http://systransis.cn/yun/102264.html

相關(guān)文章

  • 【用故事解讀 MobX源碼(一)】 autorun

    摘要:隨后,執(zhí)行官給出一張當張三存款發(fā)生變化之時,此機構(gòu)的運作時序圖的確,小機構(gòu)靠人力運作,大機構(gòu)才靠制度運轉(zhuǎn)。第一條語句創(chuàng)建觀察員第一條語句張三我們調(diào)用的時候,就創(chuàng)建了對象,對象的所有屬性都將被拷貝至一個克隆對象并將克隆對象轉(zhuǎn)變成可觀察的。 ================前言=================== 初衷:網(wǎng)上已有很多關(guān)于 MobX 源碼解讀的文章,但大多閱讀成本甚高。...

    qieangel2013 評論0 收藏0
  • 高性能JavaScript閱讀簡記(一)

    摘要:對于直接量和局部變量的訪問性能差異微不足道,性能消耗代價高一些的是全局變量數(shù)組項對象成員。當一個函數(shù)被創(chuàng)建后,作用域鏈中被放入可訪問的對象。同樣會改變作用域鏈,帶來性能問題。 早前閱讀高性能JavaScript一書所做筆記。 一、Loading and Execution 加載和運行 從加載和運行角度優(yōu)化,源于JavaScript運行會阻塞UI更新,JavaScript腳本的下載、解析...

    sorra 評論0 收藏0
  • 高性能JavaScript閱讀簡記(一)

    摘要:對于直接量和局部變量的訪問性能差異微不足道,性能消耗代價高一些的是全局變量數(shù)組項對象成員。當一個函數(shù)被創(chuàng)建后,作用域鏈中被放入可訪問的對象。同樣會改變作用域鏈,帶來性能問題。 早前閱讀高性能JavaScript一書所做筆記。 一、Loading and Execution 加載和運行 從加載和運行角度優(yōu)化,源于JavaScript運行會阻塞UI更新,JavaScript腳本的下載、解析...

    zhangrxiang 評論0 收藏0
  • 【用故事解讀 MobX 源碼(五)】 Observable

    摘要:前言初衷以系列故事的方式展現(xiàn)源碼邏輯,盡可能以易懂的方式講解源碼本系列文章用故事解讀源碼一用故事解讀源碼二用故事解讀源碼三用故事解讀源碼四裝飾器和用故事解讀源碼五文章編排每篇文章分成兩大段,第一大段以簡單的偵探系列故事的形式講解所涉及人物場 ================前言=================== 初衷:以系列故事的方式展現(xiàn) MobX 源碼邏輯,盡可能以易懂的方式...

    leeon 評論0 收藏0
  • 【用故事解讀 MobX 源碼(四)】裝飾器 和 Enhancer

    摘要:所以這是一篇插隊的文章,用于去理解中的裝飾器和概念。因此,該的作用就是根據(jù)入?yún)⒎祷鼐唧w的描述符。其次局部來看,裝飾器具體應用表達式是,其函數(shù)簽名和是一模一樣。等裝飾器語法,是和直接使用是等效等價的。 ================前言=================== 初衷:以系列故事的方式展現(xiàn) MobX 源碼邏輯,盡可能以易懂的方式講解源碼; 本系列文章: 《【用故事解...

    maybe_009 評論0 收藏0

發(fā)表評論

0條評論

最新活動
閱讀需要支付1元查看
<