去年底退伍之後進入新公司開始工作,近一年來都在 Vue / Vuex / Vue-Router 之間打滾,也寫出一些心得了,就在睽違一年多之後可以來寫新的東西了 XD。
大部分人透過 Vue 開發 SPA(Single Page Application)時通常都會搭配 Vuex 一起使用,如果不知道 Vuex 的作用的話那可以先去參考一下官方介紹。
以下範例均來自於官方文件並依據本文加以修改、調整與翻譯
因為官方的文件中都有講解跟範例了,所以 mutation 跟 action 及 getters 的作用就不多提了,以下就大概提一下 state 的部分。
在 Vuex 中都會有個 State,裡面包含了儲存在 Vuex 中的所有資料,大致長得像這樣:
const store = new Vuex.Store({state: {count: 0,},mutations: {increment(state) {state.count++;},},});
不過在一般專案開發不可能只有一兩個變數而已,所以會切分成好幾個 module
,大致如下:
const moduleA = {state: { ... },mutations: { ... },actions: { ... },getters: { ... }}const moduleB = {state: { ... },mutations: { ... },actions: { ... }}const store = new Vuex.Store({modules: {a: moduleA,b: moduleB}})
如果你想存取 state 或是 dispatch actions 時會如下:
store.state.a; // -> `moduleA`'s statestore.state.b; // -> `moduleB`'s statethis.$store.dispatch('ActionOfModuleA'); // -> `moduleA`'s actionthis.$store.dispatch('ActionOfModuleB'); // -> `moduleB`'s action
因爲將多個 module 併入一個 Vuex Store 時,actions 及 mutations 都是在 root space 中,所以一般會為了避免 actions / mutations 的命名衝突,會自己加上 namespace 或是另外去建立這些名稱的 constants:
// mutation-types.jsexport const SOME_MUTATION_A = 'A/SOME_MUTATION'export const SOME_MUTATION_B = 'B/SOME_MUTATION'// store.jsimport Vuex from 'vuex'import { SOME_MUTATION_A, SOME_MUTATION_B } from './mutation-types'const store = new Vuex.Store({modules: {moduleA: {state: { ... },mutations: {// 我們可以使用 ES2015 的 computed property name 功能定義一個 function name[SOME_MUTATION_A] (state) {// mutate state}}}moduleB: {state: { ... },mutations: {[SOME_MUTATION_B] (state) {// mutate state}}}}})
Vuex 在 2.1.0
版時在 module
中加入了 namespaced
的選項,透過啟用這個選項,Vuex 會自動幫你在 module 的 actions / mutations / getters 加上 namespace 的 prefix:
const moduleA = {namespced: true,state: { ... },mutations: { ... },actions: { ... },getters: { ... }}const moduleB = {namespced: true,state: { ... },mutations: { ... },actions: { ... }}const store = new Vuex.Store({modules: {a: moduleA,b: moduleB}})store.state.a // -> `moduleA`'s statestore.state.b // -> `moduleB`'s statethis.$store.dispatch('a/ActionOfModuleA') // -> `moduleA`'s actionthis.$store.dispatch('b/ActionOfModuleB') // -> `moduleB`'s action
除此之外,namespaced 也支援巢狀的 module,e.g.
const store = new Vuex.Store({modules: {a: {state: { ... },mutations: { ... },actions: { ... },modules: {moduleC}},b: moduleB}})
這邊稍微講一下 Vuex 的 namespacing 做了哪些事情:
// beforeconst store = new Vuex.Store({modules: {account: {namespaced: true,// module assetsstate: { ... }, // module 的 state 已經是巢狀的,不會受到 namespace 影響getters: {isAdmin () { ... } // -> getters['account/isAdmin']},actions: {login () { ... } // -> dispatch('account/login')},mutations: {login () { ... } // -> commit('account/login')},// nested modulesmodules: {// 從父 module 繼承 namespacemyPage: {state: { ... },getters: {profile () { ... } // -> getters['account/profile']}},// 進一步的巢狀 namespaceposts: {namespaced: true,state: { ... },getters: {popular () { ... } // -> getters['account/posts/popular']}}}}}})
在 module 內的所有 actions、mudations、state 會自動加上 namespace
如果你要在 locale module 使用其他 module 的 actions / mutations,請在 dispatch
/ commit
的第三個參數帶入 { root: true }
modules: {foo: {namespaced: true,getters: {// `getters` 是 module 內的其他 getters,如果要存取 rootGetters 可以使用第四個參數someGetter (state, getters, rootState, rootGetters) {getters.someOtherGetter // -> 'foo/someOtherGetter'rootGetters.someOtherGetter // -> 'someOtherGetter'rootGetters['bar/someOtherGetter'] // -> 'bar/someOtherGetter'},someOtherGetter: state => { ... }},actions: {// dispatch 及 commit 是會自動加上 module 內的 namespace// 如果要存取 root dispatch 或 commit 可以加入 `root` 選項someAction ({ dispatch, commit, getters, rootGetters }) {getters.someGetter // -> 'foo/someGetter'rootGetters.someGetter // -> 'someGetter'dispatch('someOtherAction') // -> 'foo/someOtherAction'dispatch('someOtherAction', null, { root: true }) // -> 'someOtherAction'commit('someMutation') // -> 'foo/someMutation'commit('someMutation', null, { root: true }) // -> 'someMutation'},someOtherAction (ctx, payload) { ... }}}}
以下就講解一下 Vuex 的 helpers 在遇到 namespaced 的用法
computed: {...mapState({a: state => state.some.nested.module.a, // this.ab: state => state.some.nested.module.b // this.b})},// tocomputed: {...mapState('some/nested/module', {a: state => state.a, // this.ab: state => state.b // this.b}),// or...mapState('some/nested/module', ['a', // this.a'b' // this.b])// alias...mapActions('some/nested/module', {dataA: 'a', // this.dataAdataB: 'b' // this.dataB})},
methods: {...mapActions(['some/nested/module/foo', // this.foo()'some/nested/module/bar' // this.bar()])}// tomethods: {...mapActions('some/nested/module', ['foo', // this.foo()'bar' // this.bar()]),// alias...mapActions('some/nested/module', {fooA: 'foo', // this.fooA();barA: 'bar' // this.barA()})}
與 mapActions
用法相同
2.4.0
加入的新 helper
← Back to Homeimport { createNamespacedHelpers } from 'vuex'const { mapState, mapActions } = createNamespacedHelpers('some/nested/module')export default {computed: {// look up in `some/nested/module`...mapState({a: state => state.a, // this.ab: state => state.b // this.b})// or...mapState(['a', // this.a'b' // this.b])},methods: {// look up in `some/nested/module`...mapActions(['foo', // this.foo()'bar' // this.bar()])}}