<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

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

          共 4479字,需瀏覽 9分鐘

           ·

          2021-07-14 15:11

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

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

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

          3. 應(yīng)用層通過發(fā)送信號(一般稱 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)有很多解析的文章,沒必要贅述。本文僅就 中心化、信號機(jī)制、數(shù)據(jù)流 三個(gè)點(diǎn)的實(shí)現(xiàn)上展開,討論一下 Vuex 實(shí)現(xiàn)上的缺陷。

          中心化

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

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

          • 偵聽器接口: subscribe

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

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

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

          官方實(shí)現(xiàn)的 store 非常復(fù)雜,耦合了許多邏輯。簡便起見,我們刨除各種旁路邏輯,只關(guān)注Flux架構(gòu)的中心化、信號控制機(jī)制,可以總結(jié)出一份非常簡單的實(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ā)事先注冊的_mutations方法。

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

          • 使用簡單對象作為 state

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

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

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

          信號機(jī)制

          Vuex 提供了兩個(gè)與信號有關(guā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 無返回值。

          這樣的設(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í)提升了便利性,但對初學(xué)者而言,可能導(dǎo)致如下反模式:

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

          • 造成“直接提交mutation即可”的假象,破壞了Flux的信號機(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ù)非常簡單,代碼邏輯可梳理為:

          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 對象的屬性。值得注意的一點(diǎn)是,res[key]一般作為函數(shù)掛載在外部對象,此時(shí)函數(shù)的this指向掛載的 Vue 組件。

          mapGetter

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

          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 對象托管到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),會導(dǎo)致各種子屬性回調(diào)失效,即不可能實(shí)現(xiàn)immutable特性

          后語

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

          最后



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

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

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

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


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



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

          手機(jī)掃一掃分享

          分享
          舉報(bào)

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

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  国产特级黄色视频 | 爱搞亚洲| 97成人人妻一区二区三区 | 午夜精品成人片免费 | 大香蕉综合网 |