在 深刻 vuex原理(上) 中,咱們回答了 「vuex的store是如何注入到組件中的?」 的問題,下面咱們繼續對如下問題進行探討!javascript
本篇文章主要討論 "vuex的state 和 getter 是如何映射到 各個組件實例中自動更新的?" 這個問題。首先,咱們對問題進行簡單剖析!前端
注:本篇文章只展現關鍵核心代碼,一來因爲篇幅緣由,二來展現核心代碼更容易讓人理解!再者,本篇屬於 vuex 高級篇,對於本篇章中 涉及的 vue 相關的機制 以及 vuex的 高級使用 等 不進行過多贅述!請自行前往官網查看!vue
疑問:vuex的state 和 getter 是如何映射到各個組件實例中自動更新的呢?java
該問題的核心問題是當store中的 state 和 getter 方式變動時,vuex如何保證各個組件實例中的數據自動更新,並update 組件!簡言之,某一組件store更新時,如何通知其餘組件進行數據更新,和UI更新!經過簡單分析可知,問題的根本就是組件通訊的問題!vuex
從分析可知,要解答本篇疑問,咱們須要從 vue 組件通訊談起! 在使用vue的過程當中,須要頻繁的進行組件間通訊!通訊的主體之間的關係能夠是 父子組件,也能夠是 相似 兄弟組件 或者是 無關組件 等非父子組件。 總的來講 有以下幾種方式!cookie
this.$parent
與 this.$children
v-bind="$attrs"
和 v-on="$listeners"
$dispatch 和 $broadcast
: 在vue1中使用下圖展現了 上述 方案 1~6 的組件通訊方式。 app
因爲 方案 8 官方已經不建議在vue2中使用,此處不作贅述;而方案9~10 不屬於vue特性,而是前端通用的數據通訊方案,此處也不進行贅述!若有其它方案,歡迎補充!ide
下面,咱們主要介紹一下與本文內容息息相關的方案7,中央事件總線的解決方案!其核心設計思想是引入中央通訊橋樑——中央事件總線,使各個組件只與其進行通訊,達到數據同步的通訊目的!以下圖 組件A數據變動,通知中央事件總線,其餘組件監聽並接收變動的數據。函數
下面代碼展現了在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對象的特性(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原理探究 請前往深刻vuex原理(上)