深入 Vuex 原理(下)
點選箭頭處 “藍色字” ,關注我們哦!!
孔維國,2016年加入去哪兒網技術團隊。目前在大住宿事業部/增值業務研發中心,參與開發了TMC、CRM、QTA、Auth等專案,負責node框架nomi的設計以及開發。樂於探索佈道新技術,在大前端的道路上越走越偏!在node領域上越陷越深!
前言
在深入Vuex原理(上)中,我們回答了 “Vuex 的 Store 是如何注入到元件中的?” 的問題,下面我們繼續對以下問題進行探討。 Vuex 的 state 和 getter 是如何對映到各個元件例項中自動更新的? 首先,我們對問題進行簡單剖析。
注:本篇文章只展示關鍵核心程式碼,一來由於篇幅原因,二來展示核心程式碼更容易讓人理解。再者,本篇屬於 Vuex 高階篇,對於本篇章中涉及的 Vue 相關的機制以及 Vuex 的高階使用等不進行過多贅述,請自行前往官網檢視。
準備
疑問:Vuex 的state 和 getter 是如何對映到各個元件例項中自動更新的呢?
問題剖析
該問題的核心問題是當 Store 中的 state 和 getter 方式變更時,Vuex 如何保證各個元件例項中的資料自動更新,並 update 元件?簡言之,某一元件 Store 更新時,如何通知其他元件進行資料更新,和 UI 更新。通過簡單分析可知,問題的根本就是元件通訊的問題。
淺談元件通訊
從分析可知,要解答本篇疑問,我們需要從 Vue 元件通訊談起。在使用 Vue 的過程中,需要頻繁的進行元件間通訊,通訊的主體之間的關係可以是父子元件,也可以是類似兄弟元件或者是無關元件等非父子元件。總的來說有如下幾種方式: 1.通過 props 向子元件傳遞資料:父->子; 2.通過事件向父元件傳送訊息:子->父,使用 $emit 傳送事件; 3.父鏈和子索引:this.$parent 與 this.$children; 4.依賴注入:provide 和 inject; 5.子元件引用:ref與$refs; 6.特性繫結:v-bind="$attrs" 和 v-on="$listeners"; 7.vent bus; 8.$dispatch 和 $broadcast:在 Vue1 中使用; 9.利用全域性變數 storage、cookie、query、hash 等傳遞資料: 非 Vue 特性,不做贅述; 10.全域性事件廣播。 下圖展示了上述方案1~6的元件通訊方式。
由於方案8官方已經不建議在 Vue2 中使用,此處不做贅述;而方案9~10不屬於 Vue 特性,而是前端通用的資料通訊方案,此處也不進行贅述,如有其它方案,歡迎補充。 下面,我們主要介紹一下與本文內容息息相關的方案7,中央事件匯流排的解決方案。其核心設計思想是引入中央通訊橋樑——中央事件匯流排,使各個元件只與其進行通訊,達到資料同步的通訊目的。如下圖 元件 A 資料變更,通知中央事件匯流排,其他元件監聽並接收變更的資料。
下面程式碼展示了在 Vue 中使用中央事件匯流排進行元件通訊:
Vue 的中央事件匯流排的實現簡單講就是新建了一個 Vue 物件,藉助 Vue 物件的特性($emit 與 $on)作為其他元件的通訊橋樑,實現元件間的通訊以及資料共享。
探祕原理
本部分將針對以上疑問,通過原始碼分析,剖析核心程式碼,對問題進行解答。
使用
我們探討的是 state 和 getter,首先我們先來看一下在 Vue 元件中如何方便的獲取 Vuex 的 state 和 getter 吧。
正如我們所知的,Vuex 的 Store 會劃分出 state 和 getters 兩個資料區。getter 是從 store 的 state 中派生出的狀態。程式碼如下:
原始碼分析
首先,我們先來看 state。從原始碼中我們找到了 state 的 get 方法,如下:
從原始碼得知,在 Vue 元件中,使用 this.$store.getters.xxx 獲取 xxx 屬性時,實際上是獲取的 store._vm.data.$$state 物件上的同名屬性。那麼我們將關注點放在 _vm 上。我們通過 Store 的 constructor 找到了處理 state 和 getter 的核心函式 resetStoreVM(this, state)。其核心程式碼如下:
上述程式碼初始化了一個 Vue 例項 _vm,由於 Vue 的 data 是響應式的,所以,$$state 也是響應式的,那麼當我們在一個元件例項中對 state.xxx 進行 更新時,基於 Vue 的 data 的響應式機制,所有相關元件的 state.xxx 的值都會自動更新,UI 自然也會自動更新!可見,這和 Vue 的中央事件匯流排 設計思想如出一轍,同樣藉助 Vue 物件特性(響應式的data)作為其他元件的通訊橋樑,實現元件間的通訊 以及資料共享。
總結
Vuex 的 state 是藉助 Vue 的響應式 data 實現的。設計思想與 Vue 中央事件匯流排如出一轍。
猜測
Vue 中 computed 從 data 中派生出的計算屬性,Vuex 中 getter 是從 state 中派生出的屬性;而 Vuex 中的 state 是藉助 data 實現的,那麼 getter 是否是藉助 computed 實現的呢?
原始碼驗證
在 resetStoreVM 中可以看到對於 wrappedGetters(經過包裝,收集的 getters,處理細節忽略)的處理。
上述程式碼,對 wrappedGetters 進行處理,讓 getter 儲存至 computed 物件上,下面我們關注後續 computed 的處理即可。
上述程式碼將 computed 物件掛載至 Vue 例項 _vm 的 computed 屬性上,得益於 Vue 的計算屬性特性,資料的變更同樣可以同步至其他相關元件上。這與我們的猜測一致。getter 的實現藉助了 Vue 的 computed 的特性而實現。 分析至此,我們已經得出該問題的答案。
結論
1.Vuex 的 state 是藉助 Vue 的響應式 data 實現的。 2.getter 是藉助 Vue 的計算屬性 computed 特性實現的。 3.其設計思想與 Vue 中央事件匯流排如出一轍。 更多 Vuex 原理探究,請前往深入Vuex原理(上)。