Credo che avere una comprensione delle motivazioni alla base di mutazioni e azioni ci permetta di giudicare meglio quando usare quale e come. Inoltre, libera il programmatore dall'onere dell'incertezza nelle situazioni in cui le "regole" diventano confuse. Dopo aver ragionato un po 'sui loro rispettivi scopi, sono giunto alla conclusione che sebbene possano esserci sicuramente modi sbagliati di usare Azioni e Mutazioni, non penso che ci sia un approccio canonico.
Prima proviamo a capire perché passiamo persino attraverso le mutazioni o le azioni.
Perché in primo luogo passare attraverso la piastra della caldaia? Perché non cambiare lo stato direttamente nei componenti?
A rigor di termini potresti cambiare state
direttamente dai tuoi componenti. Il state
è solo un oggetto JavaScript e non c'è nulla di magico che annullare le modifiche apportate ad esso.
// Yes, you can!
this.$store.state['products'].push(product)
Tuttavia, facendo questo spargi le tue mutazioni di stato in tutto il luogo. Si perde la possibilità di aprire semplicemente un singolo modulo che ospita lo stato e, a colpo d'occhio, vedere quale tipo di operazioni può essere applicato ad esso. Avere mutazioni centralizzate risolve questo, anche se a scapito di alcune piastre di caldaia.
// 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)
}
...
Penso che se sostituisci qualcosa di corto con il boilerplate, vuoi che anche il plateplate sia piccolo. Presumo quindi che le mutazioni debbano essere involucri molto sottili attorno alle operazioni native sullo stato, con quasi nessuna logica aziendale. In altre parole, le mutazioni sono pensate per essere usate principalmente come setter.
Ora che hai centralizzato le tue mutazioni, hai una migliore visione d'insieme dei cambiamenti di stato e poiché i tuoi strumenti (vue-devtools) sono anche consapevoli di quella posizione, questo rende il debugging più semplice. Vale anche la pena ricordare che molti plugin di Vuex non guardano direttamente lo stato per tracciare i cambiamenti, ma si affidano piuttosto alle mutazioni. Le modifiche allo stato "fuori limite" sono quindi invisibili per loro.
Quindi mutations
, actions
qual è la differenza comunque?
Le azioni, come le mutazioni, risiedono anche nel modulo del negozio e possono ricevere l' state
oggetto. Ciò implica che potrebbero anche mutarlo direttamente. Allora, qual è il punto di avere entrambi? Se riteniamo che le mutazioni debbano essere mantenute piccole e semplici, ciò implica che abbiamo bisogno di mezzi alternativi per ospitare una logica aziendale più elaborata. Le azioni sono i mezzi per farlo. E poiché come abbiamo stabilito in precedenza, vue-devtools e plugin sono consapevoli dei cambiamenti attraverso le mutazioni, per rimanere coerenti dovremmo continuare a utilizzare le mutazioni dalle nostre azioni. Inoltre, poiché le azioni sono intese come onnicomprensive e che la logica che incapsulano può essere asincrona, ha senso che anche le azioni diventerebbero asincrone dall'inizio.
Viene spesso sottolineato che le azioni possono essere asincrone, mentre le mutazioni in genere non lo sono. Puoi decidere di vedere la distinzione come un'indicazione che le mutazioni dovrebbero essere usate per qualsiasi cosa sincrona (e azioni per qualsiasi cosa asincrona); tuttavia, potresti incontrare alcune difficoltà se, ad esempio, avessi bisogno di commettere più di una mutazione (in modo sincrono) o se avessi bisogno di lavorare con un Getter delle tue mutazioni, poiché le funzioni di mutazione non ricevono né Getter né Mutazioni come argomenti ...
... il che porta a una domanda interessante.
Perché le mutazioni non ricevono Getter?
Non ho ancora trovato una risposta soddisfacente a questa domanda. Ho visto alcune spiegazioni da parte del core team che ho trovato discutibile nella migliore delle ipotesi. Se riassumo il loro utilizzo, i Getter devono essere calcolati (e spesso memorizzati nella cache) estensioni allo stato. In altre parole, sono fondamentalmente ancora lo stato, anche se richiede un certo calcolo iniziale e sono normalmente di sola lettura. Questo è almeno il modo in cui sono incoraggiati ad essere utilizzati.
Pertanto, impedire alle mutazioni di accedere direttamente a Getter significa che una delle tre cose è ora necessaria, se abbiamo bisogno di accedere al primo alcune funzionalità offerte dal secondo: (1) i calcoli di stato forniti dal Getter sono duplicati da qualche parte accessibile alla Mutazione (cattivo odore), o (2) il valore calcolato (o il relativo Getter stesso) viene tramandato come argomento esplicito alla Mutazione (funky), oppure (3) la stessa logica del Getter viene duplicata direttamente all'interno della Mutazione , senza l'ulteriore vantaggio della memorizzazione nella cache fornita dal Getter (puzza).
Di seguito è riportato un esempio di (2), che nella maggior parte degli scenari che ho riscontrato sembra l'opzione "meno male".
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
}
}
}
Per me, quanto sopra sembra non solo un po 'contorto, ma anche un po' "permeabile", poiché parte del codice presente nell'Azione sta chiaramente trasudando dalla logica interna della Mutazione.
Secondo me, questa è un'indicazione di un compromesso. Credo che consentire alle mutazioni di ricevere automaticamente Getters presenti alcune sfide. Può essere sia per la progettazione di Vuex stesso, sia per gli strumenti (vue-devtools et al), o per mantenere una certa compatibilità con le versioni precedenti o una combinazione di tutte le possibilità dichiarate.
Quello che non credo è che passare Getter alle tue Mutazioni tu sia necessariamente un segno che stai facendo qualcosa di sbagliato. Lo vedo semplicemente come "rattoppare" uno dei difetti del framework.