Vuexでは、「アクション」と「突然変異」の両方を持つロジックは何ですか?
コンポーネントのロジックは状態を変更できないことは理解していますが(これは賢いようですが)、アクションと突然変異の両方があると、ある関数を記述して別の関数をトリガーし、状態を変更しているように見えます。
「アクション」と「突然変異」の違いは何ですか、それらはどのように連携するのですか?さらに、Vuex開発者がこのようにすることを決めたのはなぜですか?
質問1:なぜVuejs開発者がこの方法でそれをすることにしたのですか?
回答:
質問2:「アクション」と「突然変異」の違いは何ですか?
最初に公式の説明を見てみましょう。
突然変異:
Vuexの突然変異は基本的にイベントです。各突然変異には名前とハンドラーがあります。
import Vuex from 'vuex' const store = new Vuex.Store({ state: { count: 1 }, mutations: { INCREMENT (state) { // mutate state state.count++ } } })
アクション:アクションは、突然変異をディスパッチする単なる関数です。
// the simplest action function increment (store) { store.dispatch('INCREMENT') } // a action with additional arguments // with ES2015 argument destructuring function incrementBy ({ dispatch }, amount) { dispatch('INCREMENT', amount) }
上記の説明は次のとおりです。
突然変異は同期的ですが、アクションは非同期的です。
別の言い方をすれば、操作が同期的な場合はアクションは不要です。それ以外の場合はアクションを実装します。
TLDRの答えは、突然変異は同期/トランザクションであることを意味していると思います。 Ajax呼び出しを実行する必要がある場合、または他の非同期コードを実行する必要がある場合は、Actionでそれを実行し、その後、新しい状態を設定するためにミューテーションをコミットする必要があります。
突然変異と行動の背後にある動機を理解することで、いつ、どのように使用するかをより適切に判断できると思います。また、「ルール」があいまいになる状況での不確実性の負担からプログラマーを解放します。それらのそれぞれの目的について推論しようとした後、間違いなくそれらを使用する間違った方法があるという結論に達しましたが、標準的なアプローチがあるとは思いません。
まず、突然変異やアクションを経験する理由を理解することから始めましょう。
そもそもボイラープレイスを通過するのはなぜですか?コンポーネントの状態を直接変更しないのはなぜですか?
厳密に言えば、コンポーネントから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
オブジェクトを受け取ることができます。これは、それらがcouldを直接変異させることも意味します。それで、両方を持つことの意味は何ですか?突然変異を小さくシンプルに保つ必要があると考えるなら、より複雑なビジネスロジックを収容する代替手段が必要であることを意味します。アクションはこれを行う手段です。また、以前のvue-devtoolsとプラグインはMutationsを介して変更を認識しているため、アクションからそれらを使用し続ける必要があります。一貫性のレベルを保証します。
アクションは非同期であることがしばしば強調されますが、突然変異は通常そうではありません。 could同期は何か(非同期の場合はアクション)に突然変異を使用する必要があることを示すものですが、たとえば、複数の突然変異をコミットする必要がある場合は困難になります。または、ゲッターによって計算された値を確認する必要がある場合、突然変異はどちらのオブジェクトも受け取りません。
これは興味深い質問につながります。
なぜ突然変異はゲッターを受け取らないのですか?
この質問に対する満足のいく答えはまだ見つかりませんでした。コアチームによる説明をいくつか見てきましたが、よくわかりませんでした。それらの使用法を要約すると、ゲッターは状態に対する計算された(そしてしばしばキャッシュされた)拡張機能を意味します。言い換えると、それらは基本的にまだ状態です。ただし、事前の計算コストと読み取り専用モードが必要です。それは少なくとも彼らが使用されるように奨励されている方法です。
したがって、突然変異がゲッターにアクセスするのを防ぐことは、後者が提供するより複雑な状態チェックをどこかで複製する必要があることを意味します。変異を呼び出す前に行うことができます(悪臭)、または発信者は、変異がゲッターを必要とし、それを渡すことを何らかの方法で知っている必要があります(ファンキー)、またはゲッターのロジックを変異の内部で複製する必要がありますキャッシング(悪臭)。
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
}
}
}
上記は少し複雑に思えます。私は、vue-devtoolsを確実に収容しながら、Vuexの設計における多くの決定が行われた可能性があることを示すものと考えました。
免責事項-私はvuejsを使用し始めたばかりなので、これは設計意図を推定するだけです。
タイムマシンのデバッグでは、状態のスナップショットを使用し、アクションと突然変異のタイムラインを表示します。理論的には、actions
をステートセッターとゲッターの記録と並べて、突然変異を同期的に記述することができます。しかしその後:
mutations
トランザクションでも発生する可能性がありますが、アクションの競合状態ではなく、トランザクションを改善する必要があると言えます。非同期プログラミングは脆弱で困難であるため、アクション内の匿名の突然変異は、これらの種類のバグをより簡単に再発見する可能性があります。次のトランザクションログを名前付きミューテーションと比較します。
Action: FetchNewsStories
Mutation: SetFetchingNewsStories
Action: FetchNewsStories [continuation]
Mutation: DoneFetchingNewsStories([...])
名前付きの変異を持たないトランザクションログの場合:
Action: FetchNewsStories
Mutation: state.isFetching = true;
Action: FetchNewsStories [continuation]
Mutation: state.isFetching = false;
Mutation: state.listOfStories = [...]
この例から、アクション内の非同期および匿名の突然変異に潜在的に追加された複雑さを推定できることを願っています。
https://vuex.vuejs.org/en/mutations.html
ここで、アプリをデバッグし、devtoolの変更ログを調べているとします。ログに記録されるすべての突然変異について、devtoolは状態の「前」と「後」のスナップショットをキャプチャする必要があります。ただし、上記の例の突然変異内の非同期コールバックはそれを不可能にします:突然変異がコミットされたときコールバックはまだ呼び出されず、実際にコールバックがいつ呼び出されるかをdevtoolが知る方法はありません-コールバックで実行された状態突然変異本質的に追跡不可能です!
docs
に従って
アクションはmutationsに似ていますが、違いは次のとおりです:
次のスニペットを検討してください。
const store = new Vuex.Store({
state: {
count: 0
},
mutations: {
increment (state) {
state.count++ //Mutating the state. Must be synchronous
}
},
actions: {
increment (context) {
context.commit('increment') //Committing the mutations. Can be asynchronous.
}
}
})
アクションハンドラー(increment)は、ストアインスタンスで同じメソッド/プロパティのセットを公開するコンテキストオブジェクトを受け取るので、context.commitを呼び出してコミットできます突然変異、またはcontext.stateおよびcontext.gettersを介して状態とゲッターにアクセスする
アクションと突然変異の主な違い:
突然変異:
Can update the state. (Having the Authorization to change the state).
アクション:
Actions are used to tell "which mutation should be triggered"
Redux Wayで
Mutations are Reducers Actions are Actions
なぜ両方??
アプリケーションが成長すると、コーディングとラインが増加します。そのとき、ミューテーションは状態を変更する唯一の権限であるため、ミューテーションではないアクションでロジックを処理する必要があります。
これも私を混乱させたので、簡単なデモを作成しました。
component.vue
<template>
<div id="app">
<h6>Logging with Action vs Mutation</h6>
<p>{{count}}</p>
<p>
<button @click="mutateCountWithAsyncDelay()">Mutate Count directly with delay</button>
</p>
<p>
<button @click="updateCountViaAsyncAction()">Update Count via action, but with delay</button>
</p>
<p>Note that when the mutation handles the asynchronous action, the "log" in console is broken.</p>
<p>When mutations are separated to only update data while the action handles the asynchronous business
logic, the log works the log works</p>
</div>
</template>
<script>
export default {
name: 'app',
methods: {
//WRONG
mutateCountWithAsyncDelay(){
this.$store.commit('mutateCountWithAsyncDelay');
},
//RIGHT
updateCountViaAsyncAction(){
this.$store.dispatch('updateCountAsync')
}
},
computed: {
count: function(){
return this.$store.state.count;
},
}
}
</script>
store.js
import 'es6-promise/auto'
import Vuex from 'vuex'
import Vue from 'vue';
Vue.use(Vuex);
const myStore = new Vuex.Store({
state: {
count: 0,
},
mutations: {
//The WRONG way
mutateCountWithAsyncDelay (state) {
var log1;
var log2;
//Capture Before Value
log1 = state.count;
//Simulate delay from a fetch or something
setTimeout(() => {
state.count++
}, 1000);
//Capture After Value
log2 = state.count;
//Async in mutation screws up the log
console.log(`Starting Count: ${log1}`); //NRHG
console.log(`Ending Count: ${log2}`); //NRHG
},
//The RIGHT way
mutateCount (state) {
var log1;
var log2;
//Capture Before Value
log1 = state.count;
//Mutation does nothing but update data
state.count++;
//Capture After Value
log2 = state.count;
//Changes logged correctly
console.log(`Starting Count: ${log1}`); //NRHG
console.log(`Ending Count: ${log2}`); //NRHG
}
},
actions: {
//This action performs its async work then commits the RIGHT mutation
updateCountAsync(context){
setTimeout(() => {
context.commit('mutateCount');
}, 1000);
}
},
});
export default myStore;
これを研究した後、私が思いついた結論は、突然変異はデータを変更することにのみ焦点を当てた慣習であり、更新されたデータの前後で懸念をより良く分離し、ロギングを改善するということです。一方、アクションは、より高いレベルのロジックを処理し、突然変異を適切に呼び出す抽象化の層です
突然変異のない状態はないからです!コミットされると、予測可能な方法で状態を変更するロジックが実行されます。突然変異は、状態を設定または変更する唯一の方法であるため(直接的な変更はありません!)、さらに、—それらは同期している必要があります。このソリューションは非常に重要な機能を推進します。突然変異はdevtoolsにログインしています。そして、それはあなたに素晴らしい読みやすさと予測可能性を提供します!
もう1つは、アクションです。言われているように—行動は突然変異を起こす。そのため、ストアを変更することはなく、これらを同期させる必要はありません。しかし、追加の非同期ロジックを管理できます!
1. docs から:
アクションは突然変異に似ていますが、違いは次のとおりです。
- 状態を変更する代わりに、アクションは変更をコミットします。
- アクションには、任意の非同期操作を含めることができます。
アクションには非同期操作を含めることができますが、変換にはできません。
2.突然変異を呼び出し、状態を直接変更できます。また、次のようにアクションを実行して状態を変更することもできます。
actions: {
increment (store) {
// do whatever ... then change the state
store.dispatch('MUTATION_NAME')
}
}
アクションはより多くのことを処理するように設計されており、そこで多くのことを実行でき(非同期操作を使用できます)、ディスパッチミューテーションによって状態を変更します。
actions
を呼び出すためだけに、mutations
の追加のレイヤーが必要ないように思えるかもしれません。例えば:
const actions = {
logout: ({ commit }) => {
commit("setToken", null);
}
};
const mutations = {
setToken: (state, token) => {
state.token = token;
}
};
actions
を呼び出すとlogout
が呼び出される場合、変異自体を呼び出さないのはなぜですか?
アクションの全体的な考え方は、1つのアクション内から複数のミューテーションを呼び出すか、Ajaxリクエストまたは想像できるあらゆる種類の非同期ロジックを作成することです。
最終的には、複数のネットワーク要求を行い、最終的に多くの異なる突然変異を呼び出すアクションが発生する可能性があります。
したがって、actions
でVuex.Store()
をできるだけ複雑にしようとすると、mutations
、state
、およびgetters
がよりクリーンでわかりやすくなり、Vueおよび_のようなライブラリーを作成するモジュール性に適合します。React人気。