前言
接下來我們則開始要研究的是 vuex 的 store 物件。
store 物件中有一個屬性叫 state。state 包含了全部的應用層級狀態。應用中的各個元件若使用了 state,則會保持與同步最新的狀態。state 就好比是 vue 中的 data,但它是整個應用的 data。
舉個簡單的例子:應用中的子元件 a 和子元件 b 用到了 state.count,兩個元件是非父子關係。這時,子元件 a 修改了 state.count,子元件 b 中的 state.count 也會相應修改。
那我們就來看看,vuex 是如何在各個元件中做到監聽 state 的屬性的。
注:本次閱讀的是 vuex 的 2.0.0 版本,原始碼請戳 這裡。
解讀
在開始閱讀原始碼之前,我一直有一個疑惑。我在元件 a 修改了 state.count,元件 b 是怎麼監聽到 state.count 的改變的?難道是 store 裡對 state 做了類似 object.defineproperty() 的處理。
帶著疑惑,開始啦.....
從哪開始切入呢?就從 store 物件被例項化的程式碼中開始吧。
import Vue from 'vue';
import Vuex from 'vuex';
Vue.use(Vuex);
const store = new Vuex.Store({
state: {
count: 0
}
});
new Vue({
el: '#app',
store,
// ...
});
複製程式碼
store 物件是通過 new Vuex.Store 被例項化出來的,開啟 src/index.js
檔案,最下面的程式碼中可以看到 vuex 暴露出的 Store 這個 API:
export default {
Store,
install,
mapState,
mapMutations,
mapGetters,
mapActions
}
複製程式碼
Store
找到 Store 這一個類,挺長的一大段程式碼。老辦法,全部方法摺疊不看,只看建構函式 constructor。開啟過濾之眼,過濾掉與 state 無關的程式碼,最後可以把程式碼簡化成這樣:
constructor (options = {}) {
const {
state = {}
} = options
// init root module.
// this also recursively registers all sub-modules
// and collects all module getters inside this._wrappedGetters
installModule(this, state, [], options)
// initialize the store vm, which is responsible for the reactivity
// (also registers _wrappedGetters as computed properties)
resetStoreVM(this, state)
}
複製程式碼
瞬間程式碼少了好多,接下來只需要關注 installModule
與 resetStoreVM
兩個方法的實現即可。緊接著,再透露一個好訊息,installModule
看了一眼,是關於 module 的初始化,等以後研究 module
再看也不遲,所以只需要研究 resetStoreVM
即可。
resetStoreVM
定位到 resetStoreVM
方法,再次過濾,其中 Vue.config.silent 去 vue 的官網上搜了一下,是取消 vue 所有的日誌與警告的功能,所以也過濾掉,剩下程式碼如下:
function resetStoreVM (store, state) {
// use a Vue instance to store the state tree
store._vm = new Vue({
data: { state }
})
}
複製程式碼
resetStoreVM
函式做的事情就是給 store 新增一個 _vm
屬性,並將 state 作為一個 vue 物件的 data,最後將這個 vue 物件賦值給 _vm。所以到這裡我們知道了 store 類的建構函式為其新增了一個 _vm 屬性。
set 與 get
建構函式解析完畢,接下來得繼續找跟 state 相關的方法,於是找到了 set
和 get
方法:
get state () {
return this._vm.state
}
set state (v) {
assert(false, `Use store.replaceState() to explicit replace store state.`)
}
複製程式碼
set
方法沒什麼好說的,意思就是 store 不給你直接修改 state(但其實是可以修改 state 物件的屬性,先不管那麼多了)。
get
方法返回了剛剛建構函式新增的 _vm 屬性的一個 data(state)。閱讀到這裡,這下我們應該知道為什麼元件 a 修改了 state.count,元件 b 也會跟著變了吧。因為 state 就是一個新的 vue 物件裡 data 的一個屬性啊。到這裡如果還不明白,是不是要回去 vue 官網重新學習 data 啦。
這樣總算是解決了我剛開始閱讀時的疑惑了。仔細思考一下,這不就是 vue 官網非父子元件的通訊中的 bus 注入到整個應用的 vue 物件中嗎?!
總結
本篇我們瞭解了 store 的 state,知道它是通過 new 一個新的 vue 物件 _vm 來監聽的,而這個 _vm 又是綁在 store 上的。所以通過這一系列的關係,最後我們能在各個元件中使用到被監聽的 this.$store.state。