Creo que tener una comprensión de las motivaciones detrás de las mutaciones y las acciones le permite a uno juzgar mejor cuándo usar qué y cómo. También libera al programador de la carga de la incertidumbre en situaciones donde las "reglas" se vuelven confusas. Después de razonar un poco sobre sus respectivos propósitos, llegué a la conclusión de que, aunque definitivamente puede haber formas incorrectas de usar acciones y mutaciones, no creo que haya un enfoque canónico.
Primero intentemos entender por qué incluso pasamos por Mutaciones o Acciones.
¿Por qué pasar por la repetitiva en primer lugar? ¿Por qué no cambiar el estado directamente en los componentes?
Estrictamente hablando, puede cambiar state
directamente desde sus componentes. El state
es sólo un objeto de JavaScript y no hay nada mágico que revertir los cambios que realice en ella.
// Yes, you can!
this.$store.state['products'].push(product)
Sin embargo, al hacer esto, está dispersando sus mutaciones de estado por todo el lugar. Pierde la capacidad de simplemente abrir un solo módulo que aloja el estado y de un vistazo ver qué tipo de operaciones se pueden aplicar a él. Tener mutaciones centralizadas resuelve esto, aunque a costa de algunas repeticiones.
// 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)
}
...
Creo que si reemplaza algo corto con repetitivo, querrá que el repetitivo también sea pequeño. Por lo tanto, supongo que las mutaciones están destinadas a ser envoltorios muy delgados alrededor de las operaciones nativas en el estado, casi sin lógica comercial. En otras palabras, las mutaciones están destinadas a ser utilizadas principalmente como setters.
Ahora que ha centralizado sus mutaciones, tiene una mejor visión general de sus cambios de estado y, dado que sus herramientas (vue-devtools) también conocen esa ubicación, facilita la depuración. También vale la pena tener en cuenta que muchos complementos de Vuex no miran el estado directamente para rastrear los cambios, sino que dependen de mutaciones para eso. Los cambios "fuera de límite" al estado son invisibles para ellos.
Entonces mutations
, actions
¿cuál es la diferencia de todos modos?
Las acciones, como las mutaciones, también residen en el módulo de la tienda y pueden recibir el state
objeto. Lo que implica que también podrían mutarlo directamente. Entonces, ¿qué sentido tiene tener ambos? Si razonamos que las mutaciones tienen que mantenerse pequeñas y simples, implica que necesitamos un medio alternativo para albergar una lógica comercial más elaborada. Las acciones son los medios para hacer esto. Y como hemos establecido anteriormente, vue-devtools y los complementos son conscientes de los cambios a través de las mutaciones, para mantener la coherencia debemos seguir usando las mutaciones de nuestras acciones. Además, dado que las acciones deben abarcar todo y que la lógica que encapsulan puede ser asíncrona, tiene sentido que las acciones también se vuelvan asincrónicas desde el principio.
A menudo se enfatiza que las acciones pueden ser asíncronas, mientras que las mutaciones generalmente no lo son. Puede decidir ver la distinción como una indicación de que las mutaciones deben usarse para cualquier cosa sincrónica (y acciones para cualquier cosa asincrónica); sin embargo, tendría algunas dificultades si, por ejemplo, necesitara cometer más de una mutación (sincrónicamente) o si necesitara trabajar con un Getter de sus mutaciones, ya que las funciones de mutación no reciben Getters ni Mutations como argumentos ...
... lo que lleva a una pregunta interesante.
¿Por qué las mutaciones no reciben Getters?
Todavía no he encontrado una respuesta satisfactoria a esta pregunta. He visto alguna explicación del equipo central de que encontré discutible en el mejor de los casos. Si resumo su uso, los Getters están destinados a ser extensiones computadas (y a menudo en caché) del estado. En otras palabras, básicamente siguen siendo el estado, aunque eso requiere un cálculo inicial y normalmente son de solo lectura. Al menos así es como se les anima a ser utilizados.
Por lo tanto, evitar que las mutaciones accedan directamente a Getters significa que ahora es necesaria una de tres cosas, si necesitamos acceder desde la primera parte de la funcionalidad ofrecida por la última: (1) los cálculos de estado proporcionados por Getter se duplican en algún lugar que sea accesible a la Mutación (mal olor), o (2) el valor calculado (o el Getter correspondiente) se transmite como un argumento explícito a la Mutación (funky), o (3) la lógica del Getter se duplica directamente dentro de la Mutación , sin el beneficio adicional de almacenamiento en caché como lo proporciona Getter (hedor).
El siguiente es un ejemplo de (2), que en la mayoría de los escenarios que he encontrado parece la opción "menos mala".
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
}
}
}
Para mí, lo anterior parece no solo un poco complicado, sino también algo "permeable", ya que parte del código presente en la Acción está claramente exudando de la lógica interna de la Mutación.
En mi opinión, esto es una indicación de un compromiso. Creo que permitir que Mutaciones reciba Getters automáticamente presenta algunos desafíos. Puede ser tanto para el diseño de Vuex como para las herramientas (vue-devtools et al), o para mantener cierta compatibilidad con versiones anteriores, o alguna combinación de todas las posibilidades establecidas.
Lo que no creo es que pasar Getters a tus mutaciones tú mismo sea necesariamente una señal de que estás haciendo algo mal. Lo veo simplemente como "parchear" una de las deficiencias del marco.