Vuexを勉強してたらMutationとActionで処理の切り分けをどうしようか、というところで悩んだので自分なりの考えを書きます。
MutationとActionの違い
そもそもMutationは同期処理でなければならなず、Actionは非同期処理も可能という違いがあります。
これは、Mutationで複数の状態の変更が非同期に行われた場合に挙動が予測不能になるのを防ぐという意図があるようです。
Actionでは、Mutationに対してcommitを行うことで間接的に状態を変更しますが、Actionを呼び出すためのdispatchメソッドはPromiseを返すため、処理の順序を制御することが可能になります。
同期処理はどっちに書く?
非同期処理はActionに書かなければないですが、同期処理はどちらに書くべきか?
ここで、Actionに渡されたtextが"Yes"ならcount+1、"No"ならcount-1、それ以外はそのままというStateについて考えてみます。
new Vuex.Store({
state: {
count: 0
},
mutations: {
plus (state, num) {
state.count += num;
},
},
actions: {
vote (context, text) {
if (text === 'Yes') {
context.commit('plus', 1);
} else if (text === 'No') {
context.commit('plus', -1);
}
},
},
}
このように、MutationはStateの変更にとどめておき、Actionで処理を行うというようにしたほうが各処理の責務が明確になっていい気がする。
(そもそも、文字列の条件分岐はComponentのほうでやったほうがいいんじゃないの、というのは置いといて)
でも、その変更処理がstateの状態に依存する変更ならMutationで処理を行いたい。
例えば、0~9までカウントアップして、9の次はまた0に戻るような処理を書く場合。
new Vuex.Store({
state: {
count: 0
},
mutations: {
countup (state, num) {
state.count = (state.count + num) % 10;
},
},
actions: {
increment (context) {
context.commit('countup', 1);
},
},
}
できるだけ、stateを直接参照するのもMutationの内側に閉じ込めてしまいたい。
Componentから直接commit
Vuexでは公式でComponentからMutationを直接commitすることを許可しています。
ただ、この図にはComponentから直接commitするルートがないんですよね。
もともとはたぶん図のほうで、Componentからの操作はActionに限定させたいという思想があったのだと思います。
ただ、それだと単純な状態の操作を作るときにコードが冗長になる(commitするだけのActionが乱立する)ので、これが許可されたのではないかと思います。
なので、できるだけAction経由で呼び出したほうがいいのではないかと思います。
mutation-types.js
Componentに対して公開するのをActionに限定するという考えを進めていくと、mutation-types.js(Mutationのタイプを定数化して外出しにするためのjs)は、action-types.jsにしたほうがよいのではないかと思えてきます。
もちろん、commitもdispatchも文字列で呼び出すことからMutationのタイプも定数で定義したほうがいいと思いますが、外に出すのはActionのほうではないかと。
結論
あくまで個人的な意見ですが
- Mutationは状態の変更のみを行う(privateな(Actionだけから呼ばれる)セッター)
- Actionは処理の公開や複雑な処理を行う(publicなメソッド)
というイメージです。
どうもJavaの考えに引きずられてる感じはありますね。