Vuex 強調使用單一狀態樹,即在一個項目里只有一個 store,這個 store 集中管理了項目中所有的數據以及對數據的操作行為。但是這樣帶來的問題是 store 可能會非常臃腫龐大不易維護,所以就需要對狀態樹進行模塊化的拆分。
?
首先貼出一個邏輯比較復雜的H5項目:DEMO?&?源碼
該項目主要包括 banner、feeds、profile 三個部分。其中 feeds 模塊最復雜,需要對數據列表進行處理,如果單條數據中是圖片:1張按照屏幕寬展示;2張各占50%;3張以上采用九宮格形式展示;如果單條數據是視頻,則顯示播放按鈕,播放一條視頻時,其他視頻暫停。
由于該項目數據、交互較多,我們使用 Vuex 對數據進行托管,只在 Vue 組件中保留最基本的操作。
如果不使用 Vuex,許多數據流需要通過 props 的方便向下傳遞,十分不便,尤其是一些跨組件的操作更加困難。使用 Vuex 后就可以將數據與操作保留在 store 中,每個組件都能輕松調用。
本項目中除了根 store 以外,還通過 module 將各組件的 store 分開管理,還不了解的同學可以往下看。
?
Module
首先介紹下基本的組件化規則:你可以根據項目組件的劃分來拆分 store,每個模塊里管理著當前組件的狀態以及行為,最后將這些模塊在根 store 進行組合。
const moduleA = {state: { ... },getters: { ... }mutations: { ... } };const moduleB = {state: { ... },getters: { ... },mutations: { ... },actions: { ... } };const store = new Vuex.Store({modules: {a: moduleA,b: moduleB} });console.log(store.state.a); // moduleA 的 state
接下來看 Vuex 核心在模塊化后的使用注意事項。
請參考上文?Vuex 核心知識 (2.0)
?
State
在 Vuex 模塊化中,state 是唯一會根據組合時模塊的別名來添加層級的,后面的 getters、mutations 以及 actions 都是直接合并在 store 下。
例如,訪問模塊 a 中的 state,要通過 store.state.a,訪問根 store 上申明的 state,依然是通過 store.state.xxx 直接訪問。
const moduleA = {state: {maState: 'A'} };const moduleB = {state: {mbState: 'B'} };const store = new Vuex.Store({modules: {a: moduleA,b: moduleB},state: {rtState: 'Root'} });console.log(store.state.a.maState); // A console.log(store.state.b.mbState); // B console.log(store.state.rtState); // Root
?
Getters
與 state 不同的是,不同模塊的 getters 會直接合并在 store.getters 下
const moduleA = {state: {count: 1},getters: {maGetter(state, getters, rootState) {return state.count + rootState.b.count;}} };const moduleB = {state: {count: 2},getters: {mbGetter() {return 'Hello Vuex';}} };const store = {modules: {a: moduleA,b: moduleB} };console.log(store.getters.maGetter); // 3 console.log(store.getters.mbGetter); // Hello Vuex
在上文我們介紹過 getters 的回調函數所接收的前兩個參數,模塊化后需要用到第三個參數——rootState。參數: 1. state,模塊中的 state 僅為模塊自身中的 state;2. getters,等同于 store.getters;3. rootState,全局 state。
通過 rootState,模塊中的 getters 就可以引用別的模塊中的 state 了,十分方便。
注意:由于 getters 不區分模塊,所以不同模塊中的 getters 如果重名,Vuex 會報出 'duplicate getter key: [重復的getter名]' 錯誤。
?
Mutations
mutations 與 getters 類似,不同模塊的 mutation 均可以通過 store.commit 直接觸發。
const moduleA = {state: {count: 1},mutations: {sayCountA(state) {console.log('Module A count: ', state.count);}} };const moduleB = {state: {count: 2},mutations: {sayCountB(state) {console.log('Module B count: ', state.count);}} };const store = {modules: {a: moduleA,b: moduleB} };store.commit('sayCountA'); // Module A count: 1 store.commit('sayCountB'); // Module B count: 2?
mutation 的回調函數中只接收唯一的參數——當前模塊的 state。如果不同模塊中有同名的 mutation,Vuex 不會報錯,通過 store.commit 調用,會依次觸發所有同名 mutation。
?
Actions
與 mutations 類似,不同模塊的 actions 均可以通過 store.dispatch 直接觸發。
const moduleA = {state: {count: 1},mutations: {sayCountA(state) {console.log('Module A count: ', state.count);}},actions: {maAction(context) {context.dispatch('mbAction');}} };const moduleB = {state: {count: 2},mutations: {sayCountB(state, num) {console.log('Module B count: ', state.count+num);}},action: {mbAction({ commit, rootState }) {commit('sayCountA');commit('sayCountB', rootState.a.count);}} };const store = {modules: {a: moduleA,b: moduleB} };store.dispatch('maAction'); // Module A count: 1、Module B count: 3
從上例可以看出,action 的回調函數接收一個 context 上下文參數,context 包含:1. state、2. rootState、3. getters、4. mutations、5. actions 五個屬性,為了簡便可以在參數中解構。
在 action 中可以通過 context.commit 跨模塊調用 mutation,同時一個模塊的 action 也可以調用其他模塊的 action。
同樣的,當不同模塊中有同名 action 時,通過 store.dispatch 調用,會依次觸發所有同名 actions。
?
最后有一點要注意的是,將 store 中的 state 綁定到 Vue 組件中的 computed 計算屬性后,對 state 進行更改需要通過 mutation 或者 action,在 Vue 組件中直接進行賦值 (this.myState = 'ABC') 是不會生效的。
?
感謝你的瀏覽,希望能有所幫助。