深刻vuex原理(下)

前言

深刻 vuex原理(上) 中,咱們回答了 「vuex的store是如何注入到組件中的?」 的問題,下面咱們繼續對如下問題進行探討!javascript

  • vuex的state 和 getter 是如何映射到 各個組件實例中自動更新的?

本篇文章主要討論 "vuex的state 和 getter 是如何映射到 各個組件實例中自動更新的?" 這個問題。首先,咱們對問題進行簡單剖析!前端


注:本篇文章只展現關鍵核心代碼,一來因爲篇幅緣由,二來展現核心代碼更容易讓人理解!再者,本篇屬於 vuex 高級篇,對於本篇章中 涉及的 vue 相關的機制 以及 vuex的 高級使用 等 不進行過多贅述!請自行前往官網查看!vue


準備

疑問:vuex的state 和 getter 是如何映射到各個組件實例中自動更新的呢?java

問題剖析

該問題的核心問題是當store中的 state 和 getter 方式變動時,vuex如何保證各個組件實例中的數據自動更新,並update 組件!簡言之,某一組件store更新時,如何通知其餘組件進行數據更新,和UI更新!經過簡單分析可知,問題的根本就是組件通訊的問題!vuex

淺談組件通訊

從分析可知,要解答本篇疑問,咱們須要從 vue 組件通訊談起! 在使用vue的過程當中,須要頻繁的進行組件間通訊!通訊的主體之間的關係能夠是 父子組件,也能夠是 相似 兄弟組件 或者是 無關組件 等非父子組件。 總的來講 有以下幾種方式!cookie

  1. 經過props向子組件傳遞數據:父 -> 子
  2. 經過事件向父組件發送消息:子 -> 父,使用$emit發送事件
  3. 父鏈 和 子索引:this.$parentthis.$children
  4. 依賴注入:provide 和 inject
  5. 子組件引用: ref與$refs
  6. 特性綁定:v-bind="$attrs"v-on="$listeners"
  7. event bus
  8. $dispatch 和 $broadcast : 在vue1中使用
  9. 利用全局變量、storage、cookie、query、hash等傳遞數據: 非vue特性,不作贅述。
  10. 全局事件廣播

下圖展現了 上述 方案 1~6 的組件通訊方式。 app

vue 組件通訊

因爲 方案 8 官方已經不建議在vue2中使用,此處不作贅述;而方案9~10 不屬於vue特性,而是前端通用的數據通訊方案,此處也不進行贅述!若有其它方案,歡迎補充!ide

下面,咱們主要介紹一下與本文內容息息相關的方案7,中央事件總線的解決方案!其核心設計思想是引入中央通訊橋樑——中央事件總線,使各個組件只與其進行通訊,達到數據同步的通訊目的!以下圖 組件A數據變動,通知中央事件總線,其餘組件監聽並接收變動的數據。函數

vue中央事件總線

下面代碼展現了在vue中使用 中央事件總線 進行組件通訊!源碼分析

let bus = new Vue({
 	methods: {
 		emit (event, ...args) {
 			this.$emit(event, ...args);
 		},
 		on (event, callback) {
 			this.$on(event, callback);
 		}
 	}
 });
 
 //component A
 bus.emit('updateData', data); // 發送數據給 bus。
 
 //component B
 bus.on('updateData', data => {
 	// 接收 updateData事件 發送的數據信息。
 })
 
複製代碼

vue的中央事件總線的實現 簡單講就是新建了一個vue對象,藉助vue對象的特性(emit 與on) 做爲其餘組件的通訊橋樑,實現組件間的通訊 以及數據共享!

探祕原理

本部分將針對以上疑問,經過源碼分析,剖析核心代碼,對問題進行解答。

使用 咱們探討的是state 和 getter,首先咱們先來看一下在vue組件中如何方便的獲取 vuex的state和getter吧!

this.$store.state.xxx;
this.$store.state.moduleA.xxx;

this.$store.getters.xxx;
this.$store.getters.moduleA.xxx;

複製代碼

正如咱們所知的,vuex的Store 會劃分出 state 和 getters 兩個數據區。getter是從store的state中派生出的狀態!代碼以下:

// 初始化store時,劃分出 state數據區 與 getters數據區
new Vuex.Store({state, getters});
複製代碼

源碼分析
首先,咱們先來看state。從源碼中咱們找到了state的get方法,以下:

get state () {
    return this._vm._data.$$state
  }

複製代碼

從源碼得知,在vue組件中 使用 this.$store.getters.xxx 獲取 xxx 屬性時,其實是獲取的store._vm.data.$$state 對象上的同名屬性。那麼咱們將關注點放在 _vm上。咱們經過Store 的constructor 找到了處理state 和 getter的核心函數resetStoreVM(this, state)。其核心代碼以下:

store._vm = new Vue({
    data: {
      $$state: 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,處理細節忽略)的處理。

const computed = {};
// 處理getters
forEachValue(wrappedGetters, (fn, key) => {
    computed[key] = () => fn(store) // 將getter存儲在computed上
    //this.$store.getters.XXX的時候獲取的是store._vm.XXX
    Object.defineProperty(store.getters, key, {
      get: () => store._vm[key], 
      enumerable: true
    })
})
  
複製代碼

上述代碼,對wrappedGetters 進行處理,讓getter 存儲至computed對象上,下面咱們關注後續computed的處理便可!

store._vm = new Vue({
    computed
  })
複製代碼

上述代碼將computed對象掛載至 vue實例 _vm的computed屬性上,得益於vue的計算屬性特性,數據的變動一樣能夠同步至其餘相關組件上。這與咱們的猜想一致!getter的實現藉助了vue的computed的特性而實現!

分析至此,咱們已經得出該問題的答案!

結論

vuex的state與getter實現原理

  1. vuex的state是藉助vue的響應式data實現的。
  2. getter是藉助vue的計算屬性computed特性實現的。
  3. 其設計思想與vue中央事件總線一模一樣。

更多vuex原理探究 請前往深刻vuex原理(上)

相關文章
相關標籤/搜索