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 state
direkt von Ihren Komponenten ändern . Das state
ist 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
, actions
was ist der Unterschied überhaupt?
Aktionen wie Mutationen befinden sich ebenfalls im Modul des Geschäfts und können das state
Objekt 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.