@nekobato さんの構造の複雑さとVuex書き分けという記事の中で複雑化したVuex構造を名前空間によって分割する手法が紹介されていましたが、名前空間分割にVuex公式が神対応してたため記事に起こしました。
TL;DR
Vuex 2.1より、Storeをモジュール分割する際にnamespaced: true
というオプションを追加することで、自動的に名前空間を付与することが出来るようになりました。
これによりtype.jsファイル等の名前空間手動管理地獄から開放されます!
名前空間の分割 (Vuex 2.1以前)
(もう使わないと思うので折りたたみました)
(構造の複雑さとVuex書き分けより抜粋)
モジュールごとにnamespaceプレフィックスを作成し、type.jsファイルで管理する手法です。
const namespace = 'hello/'
export const HELLO = namespace + 'HELLO'
export const SAY_HELLO = namespace + 'SAY_HELLO'
export const TOGGLE_DONE = namespace + 'TOGGLE_DONE'
import * as types from './types'
export default helloModule = {
state: {
hello: false,
},
getters: {
[types.HELLO] (state) {
return state.hello
}
},
actions: {
[types.SAY_HELLO] (context, payload) {
api.sayHello()
.then((data) => {})
.catch((err) => {})
}
},
mutations: {
[types.HELLO] (state, payload) {
state.hello = true
}
}
}
名前空間を分割することでより安全にアプリを成長させていけるようになりますが、typeの管理が辛いです…
react+reduxの(おお神よ…我らをビジネスロジックに集中させ給え…)感が再来していますね。
加えて、人によってはメソッド名がなんかキモいと感じるかもしれません。
名前空間の分割 (Vuex 2.1以降)
namespaced
というオプションを追加するだけでよくなりました!!!
このオプションを付けることで、state
, getters
, actions
, mutations
には自動的に自分が属しているmodule名(この場合helloModule
)と同名のnamespaceが付与されます。
// import * as types from './types' // 消滅
export default helloModule = {
namespaced: true, // 追加
state: {
hello: false,
},
getters: {
// 素直なメソッド名
hello(state) {
return state.hello
}
},
actions: {
// 素直なメソッド名
sayHello(context, payload) {
api.sayHello()
.then((data) => {})
.catch((err) => {})
}
},
mutations: {
// 素直なメソッド名
hello(state, payload) {
state.hello = true
}
}
}
呼び出すときは「mapState/mapActionsに引数で名前空間を渡す」「名前空間を明示的に指定する」のどちらのパターンでもOKです。
追記: mapStateの書き方はVuex:mapStateの書き方8パターン+11サンプルコードで詳しく扱われていたので、そちらも参考にしてみて下さい。Stateに限らずmapXxx系全般で使用できる知識です。
呼び出し側: 引数で名前空間を渡すパターン
computed: {
...mapState('helloModule', // ←名前空間を引数に渡す
// ここのstateはルートのstateではなく、自動的に名前空間が認識されてhelloModule/stateになっている
{
hello: state => state.hello
}
}
},
methods: {
...mapActions('helloModule', // ←名前空間を引数に渡す
// this.sayHello() を this.$store.dispatch('helloModule/sayHello')にマッピング
['sayHello']
)
}
呼び出し側: 名前空間を明示的に指定するパターン
computed: {
...mapState({
hello: state => state.helloModule.hello // このstateはグローバル。名前空間を掘る。
}
},
methods: {
...mapActions([
// this.sayHello() を this.$store.dispatch('helloModule/sayHello')にマッピング
'helloModule/sayHello'
])
}