web-dev-qa-db-ja.com

Vuexアクションと突然変異

Vuexでは、「アクション」と「突然変異」の両方を持つロジックは何ですか?

コンポーネントのロジックは状態を変更できないことは理解していますが(これは賢いようですが)、アクションと突然変異の両方があると、ある関数を記述して別の関数をトリガーし、状態を変更しているように見えます。

「アクション」と「突然変異」の違いは何ですか、それらはどのように連携するのですか?さらに、Vuex開発者がこのようにすることを決めたのはなぜですか?

100
Kobi

質問1:なぜVuejs開発者がこの方法でそれをすることにしたのですか?

回答:

  1. アプリケーションが大きくなり、このプロジェクトで複数の開発者が作業している場合、「状態管理」(特に「グローバル状態」)がますます複雑になることがわかります。
  2. Vuexの方法( react.jsのRedux と同様)は、状態を管理し、状態を維持し、「保存および追跡可能」(つまり、状態を変更するすべてのアクションを追跡できる新しいメカニズムを提供します- デバッグツール:vue-devtools

質問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)
}

上記の説明は次のとおりです。

  • mutation唯一の方法で状態を変更する
  • mutationはビジネスロジックを気にせず、単に「状態」を気にする
  • actionはビジネスロジックです
  • actionは、一度に複数の突然変異をdispatchできます。ビジネスロジック、それはデータの変更を気にしません(変異によって管理します)
143
Kaicui

突然変異は同期的ですが、アクションは非同期的です。

別の言い方をすれば、操作が同期的な場合はアクションは不要です。それ以外の場合はアクションを実装します。

44
Bas

TLDRの答えは、突然変異は同期/トランザクションであることを意味していると思います。 Ajax呼び出しを実行する必要がある場合、または他の非同期コードを実行する必要がある場合は、Actionでそれを実行し、その後、新しい状態を設定するためにミューテーションをコミットする必要があります。

13
Noah Namey

突然変異と行動の背後にある動機を理解することで、いつ、どのように使用するかをより適切に判断できると思います。また、「ルール」があいまいになる状況での不確実性の負担からプログラマーを解放します。それらのそれぞれの目的について推論しようとした後、間違いなくそれらを使用する間違った方法があるという結論に達しましたが、標準的なアプローチがあるとは思いません。

まず、突然変異やアクションを経験する理由を理解することから始めましょう。

そもそもボイラープレイスを通過するのはなぜですか?コンポーネントの状態を直接変更しないのはなぜですか?

厳密に言えば、コンポーネントから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のプラグインは、状態を直接監視して変更を追跡するのではなく、そのためにミューテーションに依存していることにも留意する必要があります。したがって、状態への「境界外」の変更は、それらには見えません。

mutationsactionsとにかく違いは何ですか?

突然変異などのアクションもストアのモジュールに存在し、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の設計における多くの決定が行われた可能性があることを示すものと考えました。

8
Michael Ekoka

免責事項-私は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が知る方法はありません-コールバックで実行された状態突然変異本質的に追跡不可能です!

5
ubershmekel

docs に従って

アクションmutationsに似ていますが、違いは次のとおりです:

  • 状態をmutatingする代わりに、actionscommit突然変異。
  • アクションには、任意の非同期操作を含めることができます。

次のスニペットを検討してください。

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を介して状態とゲッターにアクセスする

5
Abdullah Khan

アクションと突然変異の主な違い:

  1. アクション内では、非同期コードを実行できますが、変更はできません。そのため、非同期コードのアクションを使用するか、そうでなければ突然変異を使用します。
  2. アクション内では、ゲッター、ステート、ミューテーション(それらをコミット)、アクション(ディスパッチ)にアクセスして、ステートにアクセスできます。そのため、状態のみにアクセスする場合はミューテーションを使用し、そうでない場合はアクションを使用します。
4
roli roli

突然変異:

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

なぜ両方??

アプリケーションが成長すると、コーディングとラインが増加します。そのとき、ミューテーションは状態を変更する唯一の権限であるため、ミューテーションではないアクションでロジックを処理する必要があります。

1

これも私を混乱させたので、簡単なデモを作成しました。

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;

これを研究した後、私が思いついた結論は、突然変異はデータを変更することにのみ焦点を当てた慣習であり、更新されたデータの前後で懸念をより良く分離し、ロギングを改善するということです。一方、アクションは、より高いレベルのロジックを処理し、突然変異を適切に呼び出す抽象化の層です

1
Nathaniel Rink

突然変異のない状態はないからです!コミットされると、予測可能な方法で状態を変更するロジックが実行されます。突然変異は、状態を設定または変更する唯一の方法であるため(直接的な変更はありません!)、さらに、—それらは同期している必要があります。このソリューションは非常に重要な機能を推進します。突然変異はdevtoolsにログインしています。そして、それはあなたに素晴らしい読みやすさと予測可能性を提供します!

もう1つは、アクションです。言われているように—行動は突然変異を起こす。そのため、ストアを変更することはなく、これらを同期させる必要はありません。しかし、追加の非同期ロジックを管理できます!

0
Sumit Patel

1. docs から:

アクションは突然変異に似ていますが、違いは次のとおりです。

  • 状態を変更する代わりに、アクションは変更をコミットします。
  • アクションには、任意の非同期操作を含めることができます。

アクションには非同期操作を含めることができますが、変換にはできません。

2.突然変異を呼び出し、状態を直接変更できます。また、次のようにアクションを実行して状態を変更することもできます。

actions: {
  increment (store) {
    // do whatever ... then change the state
    store.dispatch('MUTATION_NAME')
  }
}

アクションはより多くのことを処理するように設計されており、そこで多くのことを実行でき(非同期操作を使用できます)、ディスパッチミューテーションによって状態を変更します。

0
aircraft

actionsを呼び出すためだけに、mutationsの追加のレイヤーが必要ないように思えるかもしれません。例えば:

const actions = {
  logout: ({ commit }) => {
    commit("setToken", null);
  }
};

const mutations = {
  setToken: (state, token) => {
    state.token = token;
  }
};

actionsを呼び出すとlogoutが呼び出される場合、変異自体を呼び出さないのはなぜですか?

アクションの全体的な考え方は、1つのアクション内から複数のミューテーションを呼び出すか、Ajaxリクエストまたは想像できるあらゆる種類の非同期ロジックを作成することです。

最終的には、複数のネットワーク要求を行い、最終的に多くの異なる突然変異を呼び出すアクションが発生する可能性があります。

したがって、actionsVuex.Store()をできるだけ複雑にしようとすると、mutationsstate、およびgettersがよりクリーンでわかりやすくなり、Vueおよび_のようなライブラリーを作成するモジュール性に適合します。React人気。

0
Daniel