Je crois qu'avoir une compréhension des motivations derrière les mutations et les actions permet de mieux juger quand utiliser lesquelles et comment. Cela libère également le programmeur du fardeau de l'incertitude dans les situations où les «règles» deviennent floues. Après avoir un peu réfléchi à leurs objectifs respectifs, je suis arrivé à la conclusion que, bien qu'il puisse certainement y avoir de mauvaises façons d'utiliser les actions et les mutations, je ne pense pas qu'il y ait une approche canonique.
Essayons d'abord de comprendre pourquoi nous subissons même des mutations ou des actions.
Pourquoi passer par le passe-partout en premier lieu? Pourquoi ne pas changer d'état directement dans les composants?
À proprement parler, vous pouvez modifier le state
directement depuis vos composants. Le state
n'est qu'un objet JavaScript et il n'y a rien de magique qui annulera les modifications que vous y apportez.
// Yes, you can!
this.$store.state['products'].push(product)
Cependant, en faisant cela, vous dispersez vos mutations d'état partout. Vous perdez la possibilité d'ouvrir simplement un seul module abritant l'état et de voir d'un coup d'œil quel type d'opérations peut être appliqué. Le fait d'avoir des mutations centralisées résout ce problème, mais au prix d'un passe-partout.
// 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)
}
...
Je pense que si vous remplacez quelque chose de court par du passe-partout, vous voudrez que le passe-partout soit également petit. Je suppose donc que les mutations sont censées être des enveloppes très minces autour des opérations natives sur l'état, avec presque aucune logique métier. En d'autres termes, les mutations sont destinées à être principalement utilisées comme des setters.
Maintenant que vous avez centralisé vos mutations, vous avez une meilleure vue d'ensemble de vos changements d'état et puisque vos outils (vue-devtools) sont également conscients de cet emplacement, cela facilite le débogage. Il convient également de garder à l'esprit que de nombreux plugins de Vuex ne surveillent pas directement l'état pour suivre les changements, ils s'appuient plutôt sur des mutations pour cela. Les changements d'état «hors limites» leur sont donc invisibles.
Alors mutations
, actions
quelle est la différence de toute façon?
Les actions, comme les mutations, résident également dans le module du magasin et peuvent recevoir l' state
objet. Ce qui implique qu'ils pourraient également le muter directement. Alors, quel est l'intérêt d'avoir les deux? Si nous pensons que les mutations doivent rester petites et simples, cela implique que nous avons besoin d'un moyen alternatif pour héberger une logique métier plus élaborée. Les actions sont les moyens de le faire. Et comme nous l'avons établi précédemment, vue-devtools et les plugins sont conscients des changements via les mutations, pour rester cohérent, nous devons continuer à utiliser les mutations à partir de nos actions. En outre, étant donné que les actions sont censées être toutes englobantes et que la logique qu'elles encapsulent peut être asynchrone, il est logique que les actions soient également simplement rendues asynchrones dès le départ.
Il est souvent souligné que les actions peuvent être asynchrones, alors que les mutations ne le sont généralement pas. Vous pouvez décider de voir la distinction comme une indication que les mutations doivent être utilisées pour tout ce qui est synchrone (et des actions pour tout ce qui est asynchrone); cependant, vous rencontriez des difficultés si, par exemple, vous deviez commettre plus d'une mutation (de manière synchrone), ou si vous deviez travailler avec un Getter à partir de vos mutations, car les fonctions de mutation ne reçoivent ni Getters ni Mutations comme arguments ...
... ce qui conduit à une question intéressante.
Pourquoi les mutations ne reçoivent-elles pas de Getters?
Je n'ai pas encore trouvé de réponse satisfaisante à cette question. J'ai vu des explications de l'équipe de base que je trouvais au mieux sans objet. Si je résume leur utilisation, les Getters sont censés être des extensions calculées (et souvent mises en cache) de l'état. En d'autres termes, ils sont fondamentalement toujours l'état, bien que cela nécessite un calcul initial et ils sont normalement en lecture seule. C'est du moins ainsi qu'ils sont encouragés à être utilisés.
Ainsi, empêcher les mutations d'accéder directement aux Getters signifie que l'une des trois choses est désormais nécessaire, si nous devons accéder à partir du premier à certaines fonctionnalités offertes par le second: (1) soit les calculs d'état fournis par le Getter sont dupliqués dans un endroit accessible. à la mutation (mauvaise odeur), ou (2) la valeur calculée (ou le Getter lui-même) est transmise comme argument explicite à la mutation (funky), ou (3) la logique du Getter elle-même est dupliquée directement dans la mutation , sans l'avantage supplémentaire de la mise en cache fournie par le Getter (puanteur).
Ce qui suit est un exemple de (2), qui dans la plupart des scénarios que j'ai rencontrés semble l'option "la moins mauvaise".
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
}
}
}
Pour moi, ce qui précède me semble non seulement un peu alambiqué, mais aussi quelque peu «fuyant», car une partie du code présent dans l'action suinte clairement de la logique interne de la mutation.
À mon avis, c'est le signe d'un compromis. Je pense que permettre à Mutations de recevoir automatiquement des Getters présente certains défis. Cela peut être soit pour la conception de Vuex lui-même, soit pour les outils (vue-devtools et al), ou pour maintenir une compatibilité descendante, ou une combinaison de toutes les possibilités énoncées.
Ce que je ne crois pas, c'est que passer vous-même Getters à vos mutations est nécessairement un signe que vous faites quelque chose de mal. Je le vois simplement comme un «correctif» de l'une des lacunes du cadre.