1. <strong id="7actg"></strong>
    2. <table id="7actg"></table>

    3. <address id="7actg"></address>
      <address id="7actg"></address>
      1. <object id="7actg"><tt id="7actg"></tt></object>

        源碼深度解讀: Vuex 的一些缺陷(收藏?。?/h1>

        共 4479字,需瀏覽 9分鐘

         ·

        2021-07-14 15:11

        眾所周知,Vuex 是 Flux 架構(gòu)的一種實(shí)現(xiàn)。Flux 清晰確立了數(shù)據(jù)管理場(chǎng)景下各種職能單位,其主要準(zhǔn)則有:

        1. 中心化狀態(tài)管理

        2. 狀態(tài)只能通過專門 突變 單元進(jìn)行變更

        3. 應(yīng)用層通過發(fā)送信號(hào)(一般稱 action),觸發(fā)變更

        Vuex 也是緊緊圍繞這些準(zhǔn)則開發(fā)的,通過 store 類提供 Flux 模式的核心功能。在滿足架構(gòu)的基本要求之外,則進(jìn)一步設(shè)計(jì)了許多便利的措施:

        1. 通過“模塊化”設(shè)計(jì),隔離數(shù)據(jù)單元

        2. 提供 getter 機(jī)制,提高代碼復(fù)用性

        3. 使用 Vue.$watch 方法,實(shí)現(xiàn)數(shù)據(jù)流

        4. 零配置,天然整合進(jìn) Vue 環(huán)境

        網(wǎng)上已經(jīng)有很多解析的文章,沒必要贅述。本文僅就 中心化、信號(hào)機(jī)制、數(shù)據(jù)流 三個(gè)點(diǎn)的實(shí)現(xiàn)上展開,討論一下 Vuex 實(shí)現(xiàn)上的缺陷。

        中心化

        在Vuex中,store 整合了所有功能,是對(duì)外提供的主要接口,也是Flux模式下的數(shù)據(jù)管理中心。通過它,Vuex 主要對(duì)外提供了:

        • 信號(hào)相關(guān)的: dispatch、commit

        • 偵聽器接口: subscribe

        • state 值變更接口(替換state值,不應(yīng)調(diào)用): replaceState

        • state 模型變更接口(建議僅在按需引用場(chǎng)景下使用):registerModule、unregisterModule

        • 熱更新接口(HMR邏輯,不關(guān)注):hotUpdate

        官方實(shí)現(xiàn)的 store 非常復(fù)雜,耦合了許多邏輯。簡(jiǎn)便起見,我們刨除各種旁路邏輯,只關(guān)注Flux架構(gòu)的中心化信號(hào)控制機(jī)制,可以總結(jié)出一份非常簡(jiǎn)單的實(shí)現(xiàn):

        export default class Store {
        constructor(options) {
        this._state = options.state;
        this._mutations = options.mutations;
        }

        get state() {
        return this._state;
        }

        commit(type, payload) {
        this._mutations[type].apply(this, [this.state].concat([...payload]));
        }
        }

        這是理解 Vuex 的核心,整份代碼只有兩個(gè)邏輯:

        1. 通過_state屬性實(shí)現(xiàn)中心化、自包含數(shù)據(jù)中心層。

        2. 通過 dispatch 方法,回調(diào)觸發(fā)事先注冊(cè)的_mutations方法。

        這份代碼有很多問題,舉例來說:

        • 使用簡(jiǎn)單對(duì)象作為 state

        • 狀態(tài)的突變僅僅通過修改state對(duì)象屬性值實(shí)現(xiàn)

        • 沒有任何有效的機(jī)制,防止 state 對(duì)象被誤修改

        這些設(shè)計(jì)問題,在Vuex中同樣存在,這與Vue.$watch機(jī)制有非常密切的關(guān)系(見下文),個(gè)人認(rèn)為這是極其不嚴(yán)謹(jǐn)?shù)摹?/p>

        信號(hào)機(jī)制

        Vuex 提供了兩個(gè)與信號(hào)有關(guān)的接口,其源碼可簡(jiǎn)略為:

        export default class Store {
        ...
        commit (_type, _payload, _options) {
        ...
        const entry = this._mutations[type]
        this._withCommit(() => {
        entry.forEach(function commitIterator (handler) {
        handler(payload)
        })
        })
        this._subscribers.forEach(sub => sub(mutation, this.state))
        ...
        }

        dispatch (_type, _payload) {
        ...
        const entry = this._actions[type]
        return entry.length > 1
        ? Promise.all(entry.map(handler => handler(payload)))
        : entry[0](payload)
        }
        ...
        }

        兩者之間的不同在于:

        1. dispatch 觸發(fā)的是 action 回調(diào);commit 觸發(fā)的 mutation 回調(diào)。

        2. dispatch 返回 Promise;commit 無(wú)返回值。

        這樣的設(shè)計(jì)意圖,主要還是職責(zé)分離,action 單元用于描述 發(fā)生了什么;mutation用于修改數(shù)據(jù)層狀態(tài)state。Vuex 用相似的接口,將兩者放置在相同的地位上,這一層接口設(shè)計(jì)其實(shí)存在弊病:

        1. action、mutation 各自需要一套type體系

        2. 允許應(yīng)用層繞過action,直接 commit mutation

        3. state 并非 immutable 的,而且在 action 中允許修改 state

        雖然確實(shí)提升了便利性,但對(duì)初學(xué)者而言,可能導(dǎo)致如下反模式:

        • 設(shè)計(jì)了兩套無(wú)法正交的type體系

        • 造成“直接提交mutation即可”的假象,破壞了Flux的信號(hào)機(jī)制

        • 在 action 中手誤修改了 state ,而沒有友好的跟蹤機(jī)制(這一點(diǎn)在getter中特別嚴(yán)重)

        由于沒有確切有效的機(jī)制防止錯(cuò)誤,在使用Vuex的過程中,需要非常非常警惕;需要嚴(yán)謹(jǐn)正確地使用各種職能單元;或者以規(guī)范填補(bǔ)設(shè)計(jì)上的缺陷。

        單向數(shù)據(jù)流

        這里的數(shù)據(jù)流是指從 Vuex 的 state 到 Vue 組件的props/computed/data 等狀態(tài)單元的映射,即如何在組件中獲取state。Vuex 官方推薦使用 mapGetter、mapState 接口實(shí)現(xiàn)數(shù)據(jù)綁定。

        mapState

        該函數(shù)非常簡(jiǎn)單,代碼邏輯可梳理為:

        export const mapState = normalizeNamespace((namespace, states) => {
        const res = {}
        ...
        normalizeMap(states).forEach(({ key, val }) => {
        res[key] = function mappedState() {
        ...
        return typeof val === 'function' ?
        val.call(this, state, getters) :
        state[val]
        }
        })
        ...
        return res
        })

        mapState 直接讀取 state 對(duì)象的屬性。值得注意的一點(diǎn)是,res[key]一般作為函數(shù)掛載在外部對(duì)象,此時(shí)函數(shù)的this指向掛載的 Vue 組件。

        mapGetter

        該函數(shù)同樣非常簡(jiǎn)單,其代碼邏輯為:

        export const mapGetters = normalizeNamespace((namespace, getters) => {
        const res = {}
        normalizeMap(getters).forEach(({ key, val }) => {

        res[key] = function mappedGetter() {
        ...
        return this.$store.getters[val]
        }
        ...
        })
        return res
        })

        mapGetter 訪問的則是組件掛載是 $store 實(shí)例的 getters 屬性。

        從 state 到 getter

        Vuex 的 getter屬性 與 Vue 的computed屬性在各方面的特性都非常相似,實(shí)際上,getter 正是基于 computed 實(shí)現(xiàn)的。其核心邏輯有:

        function resetStoreVM(store, state, hot) {
        ...
        store.getters = {}
        const wrappedGetters = store._wrappedGetters
        const computed = {}
        // 遍歷 getter 配置,生成 computed 屬性
        forEachValue(wrappedGetters, (fn, key) => {
        computed[key] = () => fn(store)
        Object.defineProperty(store.getters, key, {
        // 獲取 vue 實(shí)例屬性
        get: () => store._vm[key],
        enumerable: true // for local getters
        })
        })

        // 新建 Vue 實(shí)例,專門用于監(jiān)聽屬性變更
        store._vm = new Vue({
        data: {
        ?state: state
        },
        computed
        })
        ...
        }

        從代碼可以看出,Vuex 將整個(gè) state 對(duì)象托管到vue實(shí)例的data屬性中,以此換取Vue的整個(gè) watch 機(jī)制。而getter屬性正是通過返回實(shí)例的 computed 屬性實(shí)現(xiàn)的,這種實(shí)現(xiàn)方式,不可謂不精妙。問題則是:

        1. Vuex 與 Vue 深度耦合,致使不能遷移到其他環(huán)境下使用

        2. Vue 的watch機(jī)制是基于屬性讀寫函數(shù)實(shí)現(xiàn)的,如果直接替換根節(jié)點(diǎn),會(huì)導(dǎo)致各種子屬性回調(diào)失效,即不可能實(shí)現(xiàn)immutable特性

        后語(yǔ)

        Vuex 給我最大的感覺是:便利,同樣的功能有各種不同語(yǔ)義的邏輯單元處理,職責(zé)分離方面做的非常好,如果嚴(yán)格遵循規(guī)范的話,確實(shí)能非常好的組織代碼;接口也很簡(jiǎn)明易懂,對(duì)開發(fā)者非常友好。從用戶數(shù)量、影響力等方面來看,無(wú)疑是一個(gè)非常偉大的框架。這里提出來的一些觀點(diǎn)當(dāng)然也是見仁見智的,目的不外乎拋磚引玉而已。

        最后



        如果你覺得這篇內(nèi)容對(duì)你挺有啟發(fā),我想邀請(qǐng)你幫我三個(gè)小忙:

        1. 點(diǎn)個(gè)「在看」,讓更多的人也能看到這篇內(nèi)容(喜歡不點(diǎn)在看,都是耍流氓 -_-)

        2. 歡迎加我微信「 sherlocked_93 」拉你進(jìn)技術(shù)群,長(zhǎng)期交流學(xué)習(xí)...

        3. 關(guān)注公眾號(hào)「前端下午茶」,持續(xù)為你推送精選好文,也可以加我為好友,隨時(shí)聊騷。


        點(diǎn)個(gè)在看支持我吧,轉(zhuǎn)發(fā)就更好了



        瀏覽 110
        點(diǎn)贊
        評(píng)論
        收藏
        分享

        手機(jī)掃一掃分享

        分享
        舉報(bào)

        評(píng)論
        圖片
        表情
        推薦
        點(diǎn)贊
        評(píng)論
        收藏
        分享

        手機(jī)掃一掃分享

        分享
        舉報(bào)
        1. <strong id="7actg"></strong>
        2. <table id="7actg"></table>

        3. <address id="7actg"></address>
          <address id="7actg"></address>
          1. <object id="7actg"><tt id="7actg"></tt></object>
            强开小嫩苞一区二区三区图片 | 美国操逼网 | 极品嫩逼 | 亚洲综合无码 | 国产一级二级三级 | 美女扒开腿让男人桶爽久久动漫 | 一级做a爰片久久毛片A片下乡 | 簧片毛片 | 91视频蝌蚪入口 | 性videosgratis喷潮hd |