ミューテーションとアクションの背後にある動機を理解することで、いつどのようにどのように使用するかをより適切に判断できると思います。また、「ルール」が曖昧になる状況での不確実性の負担からプログラマーを解放します。それぞれの目的について少し推論した結果、アクションとミューテーションの使用方法は確かに間違っている可能性はあるものの、標準的なアプローチはないと思います。
まず、ミューテーションまたはアクションのいずれかを実行する理由を理解してみましょう。
そもそもなぜ定型を通過するのですか?コンポーネントで直接状態を変更しないのはなぜですか?
厳密に言えばstate
、コンポーネントから直接変更できます。これstate
は単なるJavaScriptオブジェクトであり、加えた変更を元に戻す魔法のようなものはありません。
// Yes, you can!
this.$store.state['products'].push(product)
ただし、これを行うと、場所全体に状態の変異が散在します。状態を格納している単一のモジュールを開くだけで、それに適用できる操作の種類を一目で確認できなくなります。突然変異を一元化すると、定型句を犠牲にしても、これを解決できます。
// so we go from this
this.$store.state['products'].push(product)
// to this
this.$store.commit('addProduct', {product})
...
// and in store
addProduct(state, {product}){
state.products.push(product)
}
...
短いものをボイラープレートに置き換える場合は、ボイラープレートも小さくしたいと思うでしょう。したがって、ミューテーションは、ビジネスロジックがほとんどない状態のネイティブ操作の非常に薄いラッパーであることを想定しています。言い換えれば、突然変異はセッターのように主に使用されることを意味します。
変異を一元化したので、状態変化の概要がわかります。ツール(vue-devtools)もその場所を認識しているため、デバッグが容易になります。また、多くのVuexのプラグインは、状態を直接監視して変更を追跡しないので、その変化に依存していることにも注意してください。したがって、状態に対する「範囲外」の変更は、それらからは見えません。
それでmutations
、actions
とにかく違いは何ですか?
ミューテーションのようなアクションもストアのモジュールにあり、state
オブジェクトを受け取ることができます。つまり、直接変更することもできます。それで、両方を持つことの意味は何ですか?ミューテーションを小さくシンプルに保つ必要があると考える場合、より複雑なビジネスロジックを収容するための代替手段が必要であることを意味します。アクションはこれを行う手段です。また、以前に確立したように、vue-devtoolsとプラグインはMutationsによる変更を認識しているため、一貫性を保つためにアクションからMutationsを使い続ける必要があります。さらに、アクションはすべてを包括することを意図しており、それらがカプセル化するロジックは非同期である可能性があるため、アクションも最初から単純に非同期にすることは理にかなっています。
アクションは非同期である可能性があることをしばしば強調しますが、ミューテーションは通常そうではありません。この違いは、ミューテーションを同期的なもの(および非同期的なアクション)に使用する必要があることを示すものと見なすこともできます。ただし、たとえば、複数のミューテーションを(同期的に)コミットする必要がある場合、またはミューテーションからゲッターを操作する必要がある場合は、ミューテーション関数がゲッターもミューテーションも引数として受け取らないため、いくつかの問題が発生します...
...興味深い質問につながります。
Mutationがゲッターを受信しないのはなぜですか?
この質問に対する満足のいく答えはまだ見つかりません。コアチームによる説明は、せいぜいわいせつなものだと思いました。それらの使用法を要約すると、ゲッターは状態に対する計算された(そして多くの場合キャッシュされた)拡張機能です。つまり、事前の計算が必要であり、通常は読み取り専用ですが、基本的にはまだ状態です。それは少なくとも彼らが使われるよう奨励されている方法です。
したがって、MutationsがGetterに直接アクセスできないようにすることは、前者から後者が提供するいくつかの機能にアクセスする必要がある場合、次の3つのうちの1つが必要であることを意味します。 Mutation(悪臭)に、または(2)計算値(または関連するGetter自体)が明示的な引数としてMutation(funky)に渡される、または(3)Getterのロジック自体がMutation内で直接複製される、ゲッター(悪臭)によって提供されるキャッシュの追加の利点なし。
以下は(2)の例です。これは、私が遭遇したほとんどのシナリオで「最も悪い」オプションと思われます。
state:{
shoppingCart: {
products: []
}
},
getters:{
hasProduct(state){
return function(product) { ... }
}
}
actions: {
addProduct({state, getters, commit, dispatch}, {product}){
// all kinds of business logic goes here
// then pull out some computed state
const hasProduct = getters.hasProduct(product)
// and pass it to the mutation
commit('addProduct', {product, hasProduct})
}
}
mutations: {
addProduct(state, {product, hasProduct}){
if (hasProduct){
// mutate the state one way
} else {
// mutate the state another way
}
}
}
私には、アクションに含まれるコードの一部がMutationの内部ロジックから明らかに染み出ているため、上記は少し複雑であるだけでなく、多少「漏れやすい」ように見えます。
私の意見では、これは妥協の兆候です。Mutationsが自動的にGetterを受信できるようにするには、いくつかの課題があると思います。これは、Vuex自体の設計、またはツール(vue-devtoolsなど)、または下位互換性を維持するため、またはすべての可能性の組み合わせのためです。
私が信じていないのは、ゲッターを自分のミューテーションに渡すことは、必然的に何かが間違っていることを示しているということです。私はそれを、フレームワークの欠点の1つを単に「パッチング」していると考えています。