Modules
由於使用單一狀態樹(single state tree),應用的所有狀態會集中到一個比較大的物件,當應用變得非常複雜時, store 物件可能變得非常臃腫。
為了解決這樣的問題, Vuex 允許我們將 store 分割成模塊(module),每個模塊擁有自己的 state、mutation、action、getter,甚至我們可以嵌套子模塊:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| const moduleA = { state: { ... }, mutations: { ... }, actions: { ... }, getters: { ... } }
const moduleB = { state: { ... }, mutations: { ... }, actions: { ... } }
const store = new Vuex.Store({ modules: { a: moduleA, b: moduleB } })
store.state.a store.state.b
|
模塊的局部狀態
mutation / getter
對於模塊內部的 mutation 和 getter,接受第一個參數是此模塊的局部 state。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| const moduleA = { state: { count: 0 }, mutations: { increment(state) { state.count++; } },
getters: { doubleCount(state) { return state.count * 2; } } };
|
模塊內部的 getter ,根節點狀態會作為第三個參數曝露出來:
1 2 3 4 5 6 7 8
| const moduleA = { getters: { sumWithRootCount(state, getters, rootState) { return state.count + rootState.count; } } };
|
action
對於模塊內部的 action ,局部狀態通過 context.state
曝露出來,根節點狀態則為 context.rootState
。
1 2 3 4 5 6 7 8 9 10
| const moduleA = { actions: { incrementIfOddOnRootSum({ state, commit, rootState }) { if ((state.count + rootState.count) % 2 === 1) { commit("increment"); } } } };
|
命名空間
在前面的範例中,模塊內部的 action 、 mutation 與 getter 是註冊在全局命名空間,這樣使得多個模塊能夠對同一個 mutation 或是 action 做出響應。
如果我們希望模塊可以有更高的封裝度與重用性,我們可以通過新增 namespaced: true
的方式使其成為命名空間模塊。當模塊被註冊後,它所有的 getter 、 action 與 mutation 都會自動根據模塊註冊的路徑調整命名。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40
| const store = new Vuex.Store({ modules: { account: { namespaced: true,
state: { ... }, getters: { isAdmin () { ... } }, actions: { login () { ... } }, mutations: { login () { ... } },
modules: { myPage: { state: { ... }, getters: { profile () { ... } } },
posts: { namespaced: true,
state: { ... }, getters: { popular () { ... } } } } } } })
|
啟用了命名空間的 getter 與 action 會收到局部化的 getter 、 dispatch 和 commit,這代表我們在使用模塊內容是不需要在同一模塊內而外添加 namespace 前綴,更改 namespace 屬性后不需要修改模塊內的代碼。
在命名空間模塊內訪問全局內容
如果你希望使用全局 state 和 getter , rootState 和 rootGetter 會作為第三和第四個參數傳入 getter ,也會通過 context 物件的屬性傳入 action 。
若需要在全局 namespace 空間內分發 action 或提交 mutation ,將 { root: true }
作為第三參數傳給 dispatch 或 commit 即可。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
| modules: { foo: { namespaced: true,
getters: { someGetter (state, getters, rootState, rootGetters) { getters.someOtherGetter rootGetters.someOtherGetter }, someOtherGetter: state => { ... } },
actions: { someAction ({ dispatch, commit, getters, rootGetters }) { getters.someGetter rootGetters.someGetter
dispatch('someOtherAction') dispatch('someOtherAction', null, { root: true })
commit('someMutation') commit('someMutation', null, { root: true }) }, someOtherAction (ctx, payload) { ... } } } }
|
帶命名空間的綁定函數
當使用 mapState
, mapGetters
, mapActions
和 mapMutations
這些函數來綁定命名空間模塊時,寫起來可能比較繁瑣:
1 2 3 4 5 6 7 8 9 10 11 12
| computed: { ...mapState({ a: state => state.some.nested.module.a, b: state => state.some.nested.module.b }) }, methods: { ...mapActions([ 'some/nested/module/foo', 'some/nested/module/bar' ]) }
|
為了簡化這樣的情況,我們可以將模塊的空間名稱字串作為第一個參數傳遞給上述函數:
1 2 3 4 5 6 7 8 9 10 11 12
| computed: { ...mapState('some/nested/module', { a: state => state.a, b: state => state.b }) }, methods: { ...mapActions('some/nested/module', [ 'foo', 'bar' ]) }
|
此外,我們可以通過使用 createNamespacedHelpers
建立命名空間 helpers:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| import { createNamespacedHelpers } from "vuex";
const { mapState, mapActions } = createNamespacedHelpers("some/nested/module");
export default { computed: { ...mapState({ a: state => state.a, b: state => state.b }) }, methods: { ...mapActions(["foo", "bar"]) } };
|
模塊的概念這邊看完可能會比較不清楚到底如何使用,未來有時間我會再新增一些範例來讓大家更清楚明白,礙於最近案子比較忙,未來有時間一定會把它寫的更清楚。