Vuex Action gegen Mutationen


173

Was ist in Vuex die Logik, sowohl "Aktionen" als auch "Mutationen" zu haben?

Ich verstehe die Logik von Komponenten, die nicht in der Lage sind, den Status zu ändern (was klug erscheint), aber sowohl Aktionen als auch Mutationen zu haben, scheint, als würden Sie eine Funktion schreiben, um eine andere Funktion auszulösen und dann den Status zu ändern.

Was ist der Unterschied zwischen "Aktionen" und "Mutationen", wie arbeiten sie zusammen und außerdem bin ich neugierig, warum die Vuex-Entwickler beschlossen haben, dies so zu tun?


2
Siehe "On To Actions", denke ich: vuex.vuejs.org/en/mutations.html#on-to-actions
Roy J


1
Sie können den Status des Geschäfts nicht direkt ändern. Die einzige Möglichkeit, den Status eines Geschäfts zu ändern, besteht darin, explizit Mutationen festzulegen. Dafür brauchen wir Maßnahmen, um Mutationen zu begehen.
Suresh Sapkota

1
@SureshSapkota diese Aussage ist sehr verwirrend, da beide mutationsund actionsin der vuex-Dokumentation als Methoden zum Ändern des Status definiert sind. Sie benötigen keine Aktion, um eine Mutation zu begehen.
Graham

1
Mutationen werden, wie der Name schon sagt, verwendet, um Ihr Statusobjekt zu ändern / zu mutieren. Aktionen sind Mutationen ziemlich ähnlich, aber anstatt den Zustand zu mutieren, begehen Aktionen Mutationen. Aktionen können beliebigen beliebigen asynchronen Code oder beliebige Geschäftslogik enthalten . Vuex empfiehlt, das Statusobjekt nur innerhalb der Mutationsfunktionen zu mutieren. Es wird auch empfohlen , keinen schweren oder blockierenden Code innerhalb der Mutationsfunktionen auszuführen, da dieser synchron ist .
Emmanuel Neni

Antworten:


221

Frage 1 : Warum haben sich die Vuejs-Entwickler dazu entschlossen?

Antworten:

  1. Wenn Ihre Anwendung groß wird und mehrere Entwickler an diesem Projekt arbeiten, wird die "Statusverwaltung" (insbesondere der "globale Status") zunehmend komplizierter.
  2. Der vuex-Weg (genau wie Redux in react.js ) bietet einen neuen Mechanismus zum Verwalten des Status, zum Beibehalten des Status und zum "Speichern und Verfolgen" ( dh jede Aktion, die den Status ändert, kann mit dem Debug-Tool verfolgt werden : vue-devtools )

Frage 2 : Was ist der Unterschied zwischen "Aktion" und "Mutation"?

Sehen wir uns zuerst die offizielle Erklärung an:

Mutationen:

Vuex-Mutationen sind im Wesentlichen Ereignisse: Jede Mutation hat einen Namen und einen Handler.

import Vuex from 'vuex'

const store = new Vuex.Store({
  state: {
    count: 1
  },
  mutations: {
    INCREMENT (state) {
      // mutate state
      state.count++
    }
  }
})

Aktionen: Aktionen sind nur Funktionen, die Mutationen auslösen.

// 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)
}

Hier ist meine Erklärung des oben Gesagten:

  • Mutation ist der einzige Weg , um den Zustand zu ändern
  • Mutation kümmert sich nicht um Geschäftslogik, sondern nur um "Zustand"
  • Aktion ist Geschäftslogik
  • Aktion können Versand zu einem Zeitpunkt mehr als 1 Mutation, es setzt nur die Business - Logik, es geht nicht um Daten schert Wechsel (die durch Mutation verwalten)

80
Die Tatsache, dass eine Aktion "die Geschäftslogik ist" und mehrere Mutationen gleichzeitig auslösen kann, ist hilfreich. Das ist die Antwort, nach der ich gesucht habe. Danke dir.
Kobi

11
Sie sagen, Sie "senden eine Mutation". Ist nicht der richtige Wortlaut, dass Sie eine Mutation begehen?
ProblemsOfSumit

4
Sie senden Aktionen aus und legen Mutationen fest.
Eirik

4
Der Versand funktioniert in Version 2.0 für Mutationen nicht mehr. Sie müssen eine Mutation in der Aktion festschreiben.
SKLTFZ

18
@Kaicui In dieser Antwort fehlt ein Hinweis darauf, dass Mutationen immer synchron sind und Aktionen möglicherweise asynchron sind. Davon abgesehen eine gute Antwort!
entscheidet

58

Mutationen sind synchron, während Aktionen asynchron sein können.

Anders ausgedrückt: Sie benötigen keine Aktionen, wenn Ihre Vorgänge synchron sind, andernfalls implementieren Sie sie.


2
Dies beantwortet tatsächlich eine Frage, die ich stellen wollte, wie das todomvc-Beispiel keine Aktionen verwendet.
Sksallaj

7
‚Sie brauchen keine Aktionen , wenn Ihre Operationen sind synchron‘ : Das ist nicht wahr: Sie tun müssen Aktionen , wenn Sie mehrere Mutationen aus dem gleichen Modul komponieren wollen, weil Sie nicht eine andere Aktion aus einer Aktion aufrufen können.
Raymundus

1
Die offensichtliche Folge dieser Antwort wäre "warum dann nicht einfach Maßnahmen ergreifen und Mutationen loswerden"
Michael Mrozek

34

Ich glaube, dass ein Verständnis der Motivationen hinter Mutationen und Aktionen es einem ermöglicht, besser zu beurteilen, wann welche und wie zu verwenden sind. Es befreit den Programmierer auch von der Last der Unsicherheit in Situationen, in denen die "Regeln" verschwimmen. Nachdem ich ein wenig über ihre jeweiligen Zwecke nachgedacht hatte, kam ich zu dem Schluss, dass es zwar definitiv falsche Wege gibt, Aktionen und Mutationen zu verwenden, aber ich glaube nicht, dass es einen kanonischen Ansatz gibt.

Versuchen wir zunächst zu verstehen, warum wir überhaupt Mutationen oder Aktionen durchlaufen.

Warum überhaupt durch die Kesselplatte gehen? Warum nicht den Status direkt in Komponenten ändern?

Genau genommen können Sie die statedirekt von Ihren Komponenten ändern . Das stateist nur ein JavaScript-Objekt und es gibt nichts Magisches, das Änderungen, die Sie daran vornehmen, rückgängig macht.

// Yes, you can!
this.$store.state['products'].push(product)

Auf diese Weise verteilen Sie jedoch Ihre Zustandsmutationen überall. Sie verlieren die Möglichkeit, einfach ein einzelnes Modul zu öffnen, in dem sich der Status befindet, und auf einen Blick zu sehen, welche Art von Operationen darauf angewendet werden können. Zentralisierte Mutationen lösen dieses Problem, allerdings auf Kosten einiger Kesselplatten.

// 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)
}
...

Ich denke, wenn Sie etwas Kurzes durch Boilerplate ersetzen, möchten Sie, dass das Boilerplate auch klein ist. Ich gehe daher davon aus, dass Mutationen sehr dünne Umhüllungen für native Operationen des Staates sein sollen, fast ohne Geschäftslogik. Mit anderen Worten, Mutationen sollen meistens wie Setter verwendet werden.

Nachdem Sie Ihre Mutationen zentralisiert haben, haben Sie einen besseren Überblick über Ihre Statusänderungen. Da Ihr Tool (vue-devtools) auch diesen Speicherort kennt, erleichtert dies das Debuggen. Es ist auch zu beachten, dass viele Vuex-Plugins den Status nicht direkt beobachten, um Änderungen zu verfolgen, sondern sich dafür eher auf Mutationen verlassen. "Out of Bound" -Änderungen des Zustands sind für sie daher unsichtbar.

Also mutations, actionswas ist der Unterschied überhaupt?

Aktionen wie Mutationen befinden sich ebenfalls im Modul des Geschäfts und können das stateObjekt empfangen . Was bedeutet, dass sie es auch direkt mutieren könnten . Was bringt es also, beides zu haben? Wenn wir der Meinung sind, dass Mutationen klein und einfach gehalten werden müssen, bedeutet dies, dass wir ein alternatives Mittel benötigen, um eine ausgefeiltere Geschäftslogik unterzubringen. Aktionen sind das Mittel, um dies zu tun. Und da vue-devtools und Plugins, wie bereits erwähnt, Änderungen durch Mutationen kennen, sollten wir weiterhin Mutationen aus unseren Aktionen verwenden, um konsistent zu bleiben. Da Aktionen alle umfassend sein sollen und die von ihnen gekapselte Logik möglicherweise asynchron ist, ist es außerdem sinnvoll, dass Aktionen von Anfang an einfach asynchron gemacht werden.

Es wird oft betont, dass Aktionen asynchron sein können, Mutationen jedoch normalerweise nicht. Sie können die Unterscheidung als Hinweis darauf betrachten, dass Mutationen für alles Synchrone verwendet werden sollten (und Aktionen für alles Asynchrone). Sie würden jedoch auf einige Schwierigkeiten stoßen, wenn Sie beispielsweise mehr als eine Mutation (synchron) festschreiben müssten oder wenn Sie mit einem Getter aus Ihren Mutationen arbeiten müssten, da Mutationsfunktionen weder Getters noch Mutationen als Argumente erhalten ...

... was zu einer interessanten Frage führt.

Warum erhalten Mutationen keine Getter?

Ich habe noch keine zufriedenstellende Antwort auf diese Frage gefunden. Ich habe eine Erklärung des Kernteams gesehen, dass ich bestenfalls Moot gefunden habe. Wenn ich ihre Verwendung zusammenfasse, sollen Getters berechnete (und häufig zwischengespeicherte) Erweiterungen des Status sein. Mit anderen Worten, sie sind im Grunde immer noch der Staat, obwohl dies einige Vorausberechnungen erfordert und sie normalerweise schreibgeschützt sind. Zumindest werden sie so dazu ermutigt, benutzt zu werden.

Um zu verhindern, dass Mutationen direkt auf Getters zugreifen, ist eines von drei Dingen erforderlich, wenn wir von ersteren auf einige von letzteren angebotene Funktionen zugreifen müssen: (1) Entweder werden die vom Getter bereitgestellten Zustandsberechnungen an einer Stelle dupliziert, auf die zugegriffen werden kann an die Mutation (schlechter Geruch) oder (2) der berechnete Wert (oder der relevante Getter selbst) wird als explizites Argument an die Mutation (funky) weitergegeben, oder (3) die Logik des Getter selbst wird direkt innerhalb der Mutation dupliziert , ohne den zusätzlichen Vorteil des Caching, wie er vom Getter (Gestank) bereitgestellt wird.

Das Folgende ist ein Beispiel für (2), das in den meisten Szenarien, auf die ich gestoßen bin, die "am wenigsten schlechte" Option zu sein scheint.

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 
        }
    }
}

Für mich scheint das oben Gesagte nicht nur ein bisschen verworren, sondern auch etwas "undicht" zu sein, da ein Teil des in der Aktion enthaltenen Codes eindeutig aus der internen Logik der Mutation sickert.

Meiner Meinung nach ist dies ein Hinweis auf einen Kompromiss. Ich glaube, dass es einige Herausforderungen darstellt, Mutationen automatisch Getters empfangen zu lassen. Dies kann entweder auf das Design von Vuex selbst oder auf das Werkzeug (vue-devtools et al.) Zurückgeführt werden oder auf die Aufrechterhaltung einer gewissen Abwärtskompatibilität oder auf eine Kombination aller angegebenen Möglichkeiten.

Was ich nicht glaube, ist, dass die Weitergabe von Getters an Ihre Mutationen notwendigerweise ein Zeichen dafür ist, dass Sie etwas falsch machen. Ich sehe es als einfaches "Patchen" eines der Mängel des Frameworks.


1
Für mich ist das die beste Antwort. Erst nachdem ich es gelesen hatte, hatte ich dieses "Klicken", das Sie fühlen, wenn Sie das Gefühl haben, etwas verstanden zu haben.
Robert Kusznier

Getter werden im Wesentlichen computedausgegeben. Sie sind schreibgeschützt. Eine schönere Möglichkeit, Mutationen anzuzeigen, besteht darin, die vorhandenen zu entfernen if else. In den Vuex-Dokumenten heißt es, dass Sie mehr als 1 commitin einer Aktion unterbringen können . Es wäre also logisch anzunehmen, dass Sie je nach Logik eine bestimmte Mutation begehen könnten. Ich betrachte Aktionen als einen Weg, um zu diktieren, WELCHE Mutation zu feuern ist.
Tamb

@Tamb: State und Getters bieten beide Kontextdaten. Es ist sinnvoll, dass sie abgefragt werden, bevor entschieden wird, wie der Staat geändert werden soll. Wenn diese Informationen vollständig aus dem Staat abgerufen werden können, ist es sinnvoll, die gesamte Logik in einer einzelnen Mutation zu kapseln, da sie Zugriff auf den Staat hat. Dies ist die Standardarbeitsanweisung für einen Setter. Was weniger sinnvoll ist, ist ein radikal anderer Ansatz, nur weil wir jetzt einen Getter nach ähnlichen Informationen abfragen müssen.
Michael Ekoka

@Tamb: Was Sie vorschlagen, ist, dass wir, wenn wir Getters abfragen müssen, das obige Muster ändern und die Auswahllogik auf eine Proxy-Aktion verschieben sollten, die auf den Getter zugreifen und eine Reihe winziger dummer Mutationen zusammenkleben kann. Es funktioniert, aber es ist immer noch umständlich und spricht nicht den schlechten Geruch an, auf den ich mich in meiner Antwort beziehe, es bewegt es einfach woanders hin.
Michael Ekoka

Die Dokumente sagen, dass Getter verwendet werden sollen, wenn Sie den Status berechnen müssen. So schien es heute richtig, dass sie den berechneten Eigenschaften ähnlich sind. Idk, worauf Sie hinaus wollen, indem Sie sagen, dass die Aktion Mutationen zusammenkleben kann. In den Dokumenten wird klar gesagt, dass Geschäftslogik in die Aktionen integriert werden soll.
Tamb

15

Ich denke, die TLDR-Antwort lautet, dass Mutationen synchron / transaktional sein sollen. Wenn Sie also einen Ajax-Aufruf ausführen oder einen anderen asynchronen Code ausführen müssen, müssen Sie dies in einer Aktion tun und anschließend eine Mutation festschreiben, um den neuen Status festzulegen.


1
Dies sieht aus wie eine Zusammenfassung der Dokumentation; woran ist nichts auszusetzen. Das Problem bei dieser Antwort ist jedoch, dass das, was sie behauptet, nicht unbedingt wahr ist. Sie können den Status innerhalb einer Mutation ändern, wenn Sie eine asynchrone Funktion / AJAX aufrufen, die dann im vollständigen Rückruf geändert werden kann. Ich denke , das ist das, was verursacht so viel Verwirrung darüber , warum Maßnahmen sollten für die besten Entwicklungspraktiken verwendet werden , wenn sie mit Vuex arbeiten. Ich weiß, dass dies sicherlich eine Quelle der Verwirrung für mich war, als ich anfing, mit Vuex zu arbeiten.
Erutan409

8

Die Hauptunterschiede zwischen Aktionen und Mutationen:

  1. Innerhalb von Aktionen können Sie asynchronen Code ausführen, jedoch nicht in Mutationen. Verwenden Sie also Aktionen für asynchronen Code, andernfalls verwenden Sie Mutationen.
  2. Innerhalb von Aktionen können Sie auf Getter, Status, Mutationen (Festschreiben), Aktionen (Versenden) in Mutationen zugreifen, auf die Sie zugreifen können. Wenn Sie also nur auf den Status zugreifen möchten, verwenden Sie Mutationen, andernfalls verwenden Sie Aktionen.

5

Laut dem docs

Aktionen ähneln Mutationen , mit folgenden Unterschieden:

  • Anstatt den Zustand zu mutieren , begehen Aktionen Mutationen.
  • Aktionen können beliebige asynchrone Operationen enthalten.

Betrachten Sie das folgende Snippet.

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.
    }
  }
})

Aktionshandler ( Inkrement ) erhalten ein Kontextobjekt, das denselben Satz von Methoden / Eigenschaften für die Speicherinstanz verfügbar macht, sodass Sie context.commit aufrufen können, um eine Mutation festzuschreiben, oder über context.state und context.getters auf den Status und die Getter zugreifen können


1
Ist ein möglicher Aufruf von der 'Mutations'-Funktion eine Methode von der vuejs-Komponente?
Alberto Acuña

@ AlbertoAcuña Ich habe die gleiche Frage, denn wenn ich das versuche, wird ein Fehler ausgegeben, dass die lokale Mutation undefiniert ist.
Rutwick Gangurde

5

Haftungsausschluss - Ich habe gerade erst angefangen, vuejs zu verwenden, daher extrapoliere ich nur die Designabsicht.

Das Debuggen von Zeitmaschinen verwendet Snapshots des Status und zeigt eine Zeitleiste mit Aktionen und Mutationen. Theoretisch hätten wir actionsneben einer Aufzeichnung von Zustandsgebern und Gettern auch die Mutation synchron beschreiben können. Aber dann:

  • Wir hätten unreine Eingaben (asynchrone Ergebnisse), die die Setter und Getter verursachten. Dies ist logisch schwer zu verfolgen und verschiedene asynchrone Setter und Getter können überraschenderweise interagieren. Das kann bei mutationsTransaktionen immer noch passieren , aber dann können wir sagen, dass die Transaktion verbessert werden muss, anstatt eine Race-Bedingung in den Aktionen zu sein. Anonyme Mutationen innerhalb einer Aktion können diese Art von Fehlern leichter wieder auftauchen lassen, da die asynchrone Programmierung fragil und schwierig ist.
  • Das Transaktionsprotokoll wäre schwer zu lesen, da es keinen Namen für die Statusänderungen geben würde. Es wäre viel mehr codeähnlich und weniger englisch, wenn die logischen Gruppierungen von Mutationen fehlen würden.
  • Es könnte schwieriger und weniger performant sein, eine Mutation auf einem Datenobjekt aufzuzeichnen, als jetzt, wo synchron definierte Diff-Punkte vorhanden sind - vor und nach dem Aufruf der Mutationsfunktion. Ich bin mir nicht sicher, wie groß das Problem ist.

Vergleichen Sie das folgende Transaktionsprotokoll mit benannten Mutationen.

Action: FetchNewsStories
Mutation: SetFetchingNewsStories
Action: FetchNewsStories [continuation]
Mutation: DoneFetchingNewsStories([...])

Mit einem Transaktionsprotokoll ohne benannte Mutationen:

Action: FetchNewsStories
Mutation: state.isFetching = true;
Action: FetchNewsStories [continuation]
Mutation: state.isFetching = false;
Mutation: state.listOfStories = [...]

Ich hoffe, Sie können aus diesem Beispiel die potenzielle zusätzliche Komplexität bei asynchronen und anonymen Mutationen innerhalb von Aktionen extrapolieren.

https://vuex.vuejs.org/en/mutations.html

Stellen Sie sich nun vor, wir debuggen die App und sehen uns die Mutationsprotokolle des Devtools an. Für jede protokollierte Mutation muss das devtool "vor" und "nach" Snapshots des Status erfassen. Der asynchrone Rückruf in der obigen Beispielmutation macht dies jedoch unmöglich: Der Rückruf wird noch nicht aufgerufen, wenn die Mutation festgeschrieben wird, und das devtool kann nicht wissen, wann der Rückruf tatsächlich aufgerufen wird - eine im Rückruf durchgeführte Statusmutation ist im Wesentlichen nicht nachverfolgbar!


4

Mutationen:

Can update the state. (Having the Authorization to change the state).

Aktionen:

Actions are used to tell "which mutation should be triggered"

Auf Redux-Art

Mutations are Reducers
Actions are Actions

Warum beides?

Wenn die Anwendung wächst, die Codierung und die Zeilen zunehmen. Dieses Mal müssen Sie die Logik in Aktionen und nicht in den Mutationen behandeln, da Mutationen die einzige Autorität sind, um den Status zu ändern. Sie sollte so sauber wie möglich sein.


2

Das hat mich auch verwirrt und ich habe eine einfache Demo gemacht.

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;

Nachdem ich dies untersucht hatte, kam ich zu dem Schluss, dass Mutationen eine Konvention sind, die sich nur darauf konzentriert, Daten zu ändern, um Bedenken besser zu trennen und die Protokollierung vor und nach den aktualisierten Daten zu verbessern. Während Aktionen eine Abstraktionsebene sind, die die übergeordnete Logik behandelt und dann die Mutationen entsprechend aufruft


0

1. Aus Dokumenten :

Aktionen ähneln Mutationen, mit folgenden Unterschieden:

  • Anstatt den Zustand zu mutieren, begehen Aktionen Mutationen.
  • Aktionen können beliebige asynchrone Operationen enthalten.

Die Aktionen können asynchrone Operationen enthalten, die Mutation jedoch nicht.

2.Wir rufen die Mutation auf, wir können den Zustand direkt ändern. und wir können auch in der Aktion Zustände wie folgt ändern:

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

Die Aktionen sind dafür ausgelegt, mehr andere Dinge zu erledigen. Wir können dort viele Dinge tun (wir können asynchrone Operationen verwenden) und dann den Status ändern, indem wir dort eine Mutation versenden.


0

Weil es keinen Zustand ohne Mutationen gibt! Beim Festschreiben wird eine Logik ausgeführt, die den Zustand auf vorhersehbare Weise ändert. Mutationen sind die einzige Möglichkeit, den Status festzulegen oder zu ändern (es gibt also keine direkten Änderungen!), Und außerdem müssen sie synchron sein. Diese Lösung bietet eine sehr wichtige Funktionalität: Mutationen melden sich bei devtools an. Und das bietet Ihnen eine gute Lesbarkeit und Vorhersehbarkeit!

Noch etwas - Aktionen. Wie gesagt - Aktionen begehen Mutationen. Sie ändern also nicht den Speicher, und es ist nicht erforderlich, dass diese synchron sind. Sie können jedoch ein zusätzliches Stück asynchroner Logik verwalten!


0

Es scheint unnötig zu sein, eine zusätzliche Ebene zu haben, um actionsnur das aufzurufen mutations, zum Beispiel:

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

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

Wenn Sie also actionsAnrufe anrufen logout, warum nicht die Mutation selbst aufrufen?

Die gesamte Idee einer Aktion besteht darin, mehrere Mutationen innerhalb einer Aktion aufzurufen oder eine Ajax-Anfrage oder eine beliebige asynchrone Logik zu stellen, die Sie sich vorstellen können.

Möglicherweise haben wir Aktionen, die mehrere Netzwerkanforderungen stellen und schließlich viele verschiedene Mutationen aufrufen.

Also versuchen wir so viel Komplexität Vuex.Store()wie möglich in unser zu stecken actionsund das lässt unsermutations , stateund getterssauberer und unkompliziert und fällt mit der Art der Modularität in Linie , die beliebten Bibliotheken wie Vue und Reagieren macht.


0

Ich benutze Vuex seit ungefähr 3 Jahren professionell und hier ist, was ich denke, ich habe über die wesentlichen Unterschiede zwischen Aktionen und Mutationen herausgefunden, wie Sie davon profitieren können, wenn Sie sie gut zusammen verwenden, und wie Sie Ihr Leben schwieriger machen können, wenn Sie benutze es nicht gut.

Das Hauptziel von Vuex ist es, ein neues Muster zur Steuerung des Verhaltens Ihrer Anwendung anzubieten: Reaktivität. Die Idee ist, die Orchestrierung des Status Ihrer Anwendung auf ein spezielles Objekt zu verlagern: ein Geschäft. Es bietet bequeme Methoden, um Ihre Komponenten direkt mit Ihren Geschäftsdaten zu verbinden und sie nach Belieben zu verwenden. Auf diese Weise können sich Ihre Komponenten auf ihre Aufgabe konzentrieren: Definieren einer Vorlage, eines Stils und eines grundlegenden Komponentenverhaltens, die Ihrem Benutzer präsentiert werden sollen. In der Zwischenzeit bewältigt der Store die hohe Datenlast.

Dies ist jedoch nicht nur der einzige Vorteil dieses Musters. Die Tatsache, dass Speicher eine einzige Datenquelle für die gesamte Anwendung sind, bietet ein großes Potenzial für die Wiederverwendbarkeit dieser Daten über viele Komponenten hinweg. Dies ist nicht das erste Muster, mit dem versucht wird, dieses Problem der komponentenübergreifenden Kommunikation zu lösen. Es zeigt sich jedoch, dass Sie gezwungen sind, ein sehr sicheres Verhalten in Ihrer Anwendung zu implementieren, indem Sie Ihren Komponenten grundsätzlich verbieten, den Status dieser gemeinsam genutzten Daten zu ändern und zwingen Sie es stattdessen, "öffentliche Endpunkte" zu verwenden, um nach Änderungen zu fragen.

Die Grundidee ist folgende:

  • Der Speicher verfügt über einen internen Status, auf den Komponenten niemals direkt zugreifen dürfen (mapState ist effektiv gesperrt).
  • Der Speicher verfügt über Mutationen, die eine synchrone Änderung des internen Zustands darstellen. Die einzige Aufgabe einer Mutation besteht darin, den Status zu ändern. Sie sollten nur von einer Aktion aufgerufen werden. Sie sollten benannt werden, um Dinge zu beschreiben, die mit dem Status passiert sind (ORDER_CANCELED, ORDER_CREATED). Halte sie kurz und süß. Sie können sie mithilfe der Browsererweiterung von Vue Devtools durchgehen (auch zum Debuggen geeignet!).
  • Der Laden hat auch Aktionen, die asynchron sein oder ein Versprechen zurückgeben sollten. Dies sind die Aktionen, die Ihre Komponenten aufrufen, wenn sie den Status der Anwendung ändern möchten. Sie sollten mit geschäftsorientierten Aktionen (Verben, dh cancelOrder, createOrder) benannt werden. Hier validieren und senden Sie Ihre Anfragen. Jede Aktion kann unterschiedliche Commits in unterschiedlichen Schritten aufrufen, wenn der Status geändert werden muss.
  • Schließlich hat der Laden Getter, mit denen Sie Ihren Status Ihren Komponenten aussetzen. Erwarten Sie, dass sie bei der Erweiterung Ihrer Anwendung in vielen Komponenten häufig verwendet werden. Vuex speichert Getter stark zwischen, um unnötige Berechnungszyklen zu vermeiden (solange Sie Ihrem Getter keine Parameter hinzufügen - versuchen Sie, keine Parameter zu verwenden). Zögern Sie also nicht, sie ausgiebig zu verwenden. Stellen Sie einfach sicher, dass Sie Namen angeben, die so genau wie möglich beschreiben, in welchem ​​Status sich die Anwendung derzeit befindet.

Davon abgesehen beginnt die Magie, wenn wir beginnen, unsere Anwendung auf diese Weise zu entwerfen. Beispielsweise:

  • Wir haben eine Komponente, die dem Benutzer eine Liste von Bestellungen mit der Möglichkeit bietet, diese Bestellungen zu löschen
  • Die Komponenten haben einen Store Getter (deletableOrders) zugeordnet, bei dem es sich um ein Array von Objekten mit IDs handelt
  • Die Komponente verfügt über eine Schaltfläche in jeder Auftragszeile, und ihr Klick wird einer Geschäftsaktion (deleteOrder) zugeordnet, die das Auftragsobjekt an sie übergibt (das, wie wir uns erinnern werden, aus der Geschäftsliste selbst stammt).
  • Die Aktion "deleteOrder speichern" führt Folgendes aus:
    • es validiert die Löschung
    • Es speichert die zu löschende Reihenfolge vorübergehend
    • Es schreibt die ORDER_DELETED-Mutation mit der Reihenfolge fest
    • Es sendet den API-Aufruf, um die Bestellung tatsächlich zu löschen (ja, NACH dem Ändern des Status!).
    • Es wartet, bis der Aufruf beendet ist (der Status ist bereits aktualisiert). Bei einem Fehler rufen wir die Mutation ORDER_DELETE_FAILED in der Reihenfolge auf, die wir zuvor beibehalten haben.
  • Die ORDER_DELETED-Mutation entfernt einfach die angegebene Reihenfolge aus der Liste der löschbaren Bestellungen (wodurch der Getter aktualisiert wird).
  • Die ORDER_DELETE_FAILED-Mutation setzt sie einfach zurück und ändert sie in den Status, um über den Fehler zu benachrichtigen (eine andere Komponente, die Fehlerbenachrichtigung, würde diesen Status verfolgen, um zu wissen, wann sie sich selbst anzeigen soll).

Am Ende haben wir eine Benutzererfahrung, die als "reaktiv" eingestuft wird. Aus Sicht unseres Nutzers wurde der Artikel sofort gelöscht. Meistens erwarten wir, dass unsere Endpunkte nur funktionieren, daher ist dies perfekt. Wenn dies fehlschlägt, haben wir immer noch die Kontrolle darüber, wie unsere Anwendung reagiert , da wir das Problem des Status unserer Front-End-Anwendung erfolgreich von den tatsächlichen Daten getrennt haben.

Sie brauchen nicht immer ein Geschäft, wohlgemerkt. Wenn Sie feststellen, dass Sie Geschäfte schreiben, die so aussehen:

export default {
  state: {
    orders: []
  },
  mutations: {
    ADD_ORDER (state, order) {
       state.orders.push(order)
    },
    DELETE_ORDER (state, orderToDelete) {
       state.orders = state.orders.filter(order => order.id !== orderToDelete.id)
    }
  },
  actions: {
    addOrder ({commit}, order) {
      commit('ADD_ORDER', order)
    },
    deleteOrder ({commit}, order) {
      commit('DELETE_ORDER', order)
    }
  },
  getters: {
    orders: state => state.orders
  }
}

Mir scheint, Sie verwenden den Speicher nur als Datenspeicher und verpassen möglicherweise den Reaktivitätsaspekt, indem Sie nicht zulassen, dass er auch die Kontrolle über Variablen übernimmt, auf die Ihre Anwendung reagiert. Grundsätzlich können und sollten Sie wahrscheinlich einige Codezeilen, die in Ihren Komponenten geschrieben sind, in Ihre Filialen auslagern.

Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.