Vuex Action vs Mutations

173

Jaka jest logika używania zarówno „akcji”, jak i „mutacji” w Vuex?

Rozumiem logikę komponentów, które nie są w stanie modyfikować stanu (co wydaje się sprytne), ale posiadanie zarówno akcji, jak i mutacji wydaje się, że piszesz jedną funkcję, aby wyzwolić inną funkcję, a następnie zmienić stan.

Jaka jest różnica między „akcjami” a „mutacjami”, jak one ze sobą współpracują, a co więcej, jestem ciekaw, dlaczego programiści Vuex zdecydowali się to zrobić w ten sposób?

Kobi
źródło
2
Zobacz „On To Actions”, myślę, że: vuex.vuejs.org/en/mutations.html#on-to-actions
Roy J
powiązana dyskusja: github.com/vuejs/vuex/issues/587
chuck911
1
Nie możesz bezpośrednio zmienić stanu sklepu. Jedynym sposobem zmiany stanu sklepu jest jawne zatwierdzenie mutacji. W tym celu potrzebujemy działań, które spowodują mutacje.
Suresh Sapkota
1
@SureshSapkota to stwierdzenie jest bardzo mylące, ponieważ oba mutationsi actionssą zdefiniowane w dokumentacji vuex jako metody zmiany stanu. Nie potrzebujesz żadnego działania, aby dokonać mutacji.
Graham
1
Mutacje, jak sama nazwa wskazuje, służą do modyfikowania / mutowania obiektu stanu. Akcje są dość podobne do mutacji, ale zamiast mutowania stanu, akcje wykonują mutacje. Akcje mogą zawierać dowolny kod asynchroniczny lub logikę biznesową . Vuex zaleca, aby obiekt stanu był mutowany tylko w funkcjach mutacji. Zaleca się również, aby nie uruchamiać żadnego ciężkiego lub blokującego kodu wewnątrz funkcji mutacji, ponieważ ma on charakter synchroniczny .
Emmanuel Neni

Odpowiedzi:

221

Pytanie 1 : Dlaczego programiści Vuejs zdecydowali się to zrobić w ten sposób?

Odpowiedź:

  1. Kiedy twoja aplikacja stanie się duża i gdy jest wielu programistów pracujących nad tym projektem, zobaczysz, że „zarządzanie stanem” (zwłaszcza „stanem globalnym”) będzie coraz bardziej skomplikowane.
  2. Sposób Vuex (podobnie jak Redux w React.js ) oferuje nowy mechanizm zarządzania stanem, utrzymywania stanu oraz „zapisywania i śledzenia” (oznacza to, że każda akcja modyfikująca stan może być śledzona przez narzędzie do debugowania: vue-devtools )

Pytanie 2 : Jaka jest różnica między „akcją” a „mutacją”?

Najpierw zobaczmy oficjalne wyjaśnienie:

Mutacje:

Mutacje Vuex są zasadniczo zdarzeniami: każda mutacja ma nazwę i obsługę.

import Vuex from 'vuex'

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

Akcje: Akcje to tylko funkcje, które wysyłają mutacje.

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

Oto moje wyjaśnienie powyższego:

  • mutacja to jedyna droga zmiany stanu
  • mutacja nie dba o logikę biznesową, po prostu dba o „stan”
  • działanie to logika biznesowa
  • akcja może wywołać więcej niż 1 mutację na raz, po prostu implementuje logikę biznesową, nie dba o zmianę danych (które zarządzają przez mutację)
Kaicui
źródło
80
Fakt, że działanie „jest logiką biznesową” i może wywołać wiele mutacji jednocześnie, jest pomocny. To jest odpowiedź, której szukałem. Dziękuję Ci.
Kobi
11
wy, cuys, mówicie, że "wysyłacie mutację". Czy nie jest poprawne sformułowanie, że POPEŁNIASZ mutację?
ProblemsOfSumit
4
Wysyłasz akcje i dokonujesz mutacji.
eirik
4
wysyłka nie działa już w vue 2.0 dla mutacji, musisz zatwierdzić mutację w akcji.
SKLTFZ
18
@Kaicui W tej odpowiedzi brakuje notatki o mutacjach, które zawsze są synchroniczne, a akcje mogą być asynchroniczne. Poza tym dobra odpowiedź!
decyduje
58

Mutacje są synchroniczne, podczas gdy akcje mogą być asynchroniczne.

Ujmując to inaczej: nie potrzebujesz akcji, jeśli twoje operacje są synchroniczne, w przeciwnym razie zaimplementuj je.

Bas
źródło
2
to właściwie odpowiada na pytanie, które zamierzałem zadać, o tym, jak przykład todomvc nie wykorzystuje działań.
sksallaj
7
„Nie potrzeba działania, jeśli operacje są synchroniczne” : To nie jest prawda: masz zrobić działania muszą jeśli chcesz komponować wiele mutacji tego samego modułu, ponieważ nie można wywołać inną akcję z akcji.
Raymundus
1
Oczywistą kontynuacją tej odpowiedzi będzie „to dlaczego nie po prostu działań i pozbyć się mutacji”
Michael Mrozek
34

Wierzę, że zrozumienie motywacji stojących za mutacjami i działaniami pozwala lepiej ocenić, kiedy użyć których i jak. Uwalnia także programistę od ciężaru niepewności w sytuacjach, gdy „reguły” stają się niejasne. Po zastanowieniu się nad ich celami doszedłem do wniosku, że chociaż zdecydowanie mogą istnieć niewłaściwe sposoby użycia akcji i mutacji, nie sądzę, że istnieje podejście kanoniczne.

Najpierw spróbujmy zrozumieć, dlaczego w ogóle przechodzimy przez mutacje lub akcje.

Po co w ogóle przechodzić przez szablon? Dlaczego nie zmienić stanu bezpośrednio w komponentach?

Ściśle mówiąc, możesz zmienić statebezpośrednio ze swoich komponentów. Jest stateto po prostu obiekt JavaScript i nie ma nic magicznego, co cofnie wprowadzone w nim zmiany.

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

Jednak robiąc to, rozpraszasz mutacje swojego stanu w każdym miejscu. Tracisz możliwość po prostu otwarcia pojedynczego modułu zawierającego stan i na pierwszy rzut oka zobaczysz, jakie operacje można na nim wykonać. Posiadanie scentralizowanych mutacji rozwiązuje ten problem, aczkolwiek kosztem pewnych schematów.

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

Myślę, że jeśli zamienisz coś krótkiego na standardowy, będziesz chciał, aby kocioł również był mały. Dlatego przypuszczam, że mutacje mają być bardzo cienkimi opakowaniami wokół natywnych operacji w państwie, prawie bez logiki biznesowej. Innymi słowy, mutacje mają być używane głównie jak setery.

Teraz, gdy scentralizowałeś swoje mutacje, masz lepszy przegląd zmian stanu, a ponieważ Twoje narzędzia (vue-devtools) są również świadome tej lokalizacji, ułatwia to debugowanie. Warto również pamiętać, że wiele wtyczek Vuex nie obserwuje stanu bezpośrednio, aby śledzić zmiany, raczej polegają na mutacjach. Zmiany stanu „poza granicami” są więc dla nich niewidoczne.

Więc mutations, actionsco jest w każdym razie różnica?

Akcje, podobnie jak mutacje, również znajdują się w module sklepu i mogą odebrać stateobiekt. Co oznacza, że mogliby również dokonać bezpośredniej mutacji. Więc jaki jest sens posiadania obu? Jeśli uznamy, że mutacje muszą być małe i proste, oznacza to, że potrzebujemy alternatywnych środków, aby pomieścić bardziej złożoną logikę biznesową. Działania są środkiem do osiągnięcia tego. A ponieważ jak ustaliliśmy wcześniej, vue-devtools i wtyczki są świadome zmian poprzez mutacje, aby zachować spójność, powinniśmy nadal używać mutacji w naszych działaniach. Ponadto, ponieważ akcje mają obejmować wszystko, a logika, którą zawierają, może być asynchroniczna, ma sens, aby akcje były po prostu asynchroniczne od samego początku.

Często podkreśla się, że akcje mogą być asynchroniczne, podczas gdy mutacje zazwyczaj nie. Możesz zdecydować, że to rozróżnienie będzie wskazówką, że mutacje powinny być używane w przypadku wszystkiego, co jest synchroniczne (i działania w przypadku wszystkiego, co jest asynchroniczne); Jednak napotkasz pewne trudności, jeśli na przykład musisz wprowadzić więcej niż jedną mutację (synchronicznie), lub jeśli potrzebujesz pracować z Getterem z twoich mutacji, ponieważ funkcje mutacji nie otrzymują ani Getterów, ani Mutacji jako argumentów ...

... co prowadzi do ciekawego pytania.

Dlaczego mutacje nie otrzymują Getters?

Nie znalazłem jeszcze satysfakcjonującej odpowiedzi na to pytanie. Widziałem wyjaśnienia głównego zespołu, które w najlepszym razie uznałem za dyskusyjne. Jeśli podsumuję ich użycie, Gettery mają być obliczanymi (i często buforowanymi) rozszerzeniami stanu. Innymi słowy, w zasadzie nadal są stanem, chociaż wymaga to pewnych obliczeń z góry i zwykle są tylko do odczytu. Przynajmniej tak są zachęcani do używania.

Tak więc, zapobieganie bezpośredniemu dostępowi do Getterów przez mutacje oznacza, że ​​jedna z trzech rzeczy jest teraz konieczna, jeśli potrzebujemy uzyskać dostęp z tej pierwszej do niektórych funkcji oferowanych przez tę drugą: (1) albo obliczenia stanu dostarczane przez Getter są zduplikowane w miejscu, które jest dostępne do Mutacji (nieprzyjemny zapach) lub (2) obliczona wartość (lub odpowiedni sam Getter) jest przekazywana jako wyraźny argument do Mutacji (funky), lub (3) sama logika Gettera jest powielana bezpośrednio w Mutacji , bez dodatkowej korzyści z buforowania, jak zapewnia Getter (smród).

Poniżej znajduje się przykład (2), który w większości scenariuszy, z którymi się spotkałem, wydaje się „najmniej złą” opcją.

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

Wydaje mi się, że powyższe wydaje mi się nie tylko trochę zagmatwane, ale także nieco „nieszczelne”, ponieważ część kodu obecnego w Akcji wyraźnie wycieka z wewnętrznej logiki Mutacji.

Moim zdaniem to zapowiedź kompromisu. Uważam, że zezwolenie mutacjom na automatyczne otrzymywanie Getterów stanowi pewne wyzwanie. Może to dotyczyć projektu samego Vuex lub narzędzi (Vue-devtools i in.), Lub zachowania pewnej kompatybilności wstecznej lub kombinacji wszystkich wymienionych możliwości.

Nie wierzę, że samodzielne przekazywanie Getterów do swoich mutacji jest koniecznie oznaką, że robisz coś złego. Widzę to jako po prostu „łatanie” jednej z wad frameworka.

Michael Ekoka
źródło
1
Dla mnie to najlepsza odpowiedź. Dopiero po przeczytaniu usłyszałem to „kliknięcie”, które czujesz, gdy czujesz, że coś zrozumiałeś.
Robert Kusznier
Gettery są zasadniczo computedwyjściem. Są tylko do odczytu. Lepszym sposobem przeglądania mutacji może być usunięcie tego, if elseco masz. Dokumenty vuex mówią, że commitw akcji możesz pomieścić więcej niż 1 . Zatem logiczne byłoby założenie, że w zależności od logiki możesz popełnić określoną mutację. Postrzegam działania jako sposób na dyktowanie, KTÓREJ mutacji ma odpalić.
Tamb
@Tamb: State i Getters oferują dane kontekstowe. Sensowne jest, aby byli pytani przed podjęciem decyzji, jak zmodyfikować państwo. Gdy ta informacja może być w całości pobrana z Państwa, sensowne jest, aby cała logika została zamknięta w jednej mutacji, ponieważ ma ona dostęp do Państwa. Jest to standardowa procedura operacyjna dla ustawiacza. To, co ma mniej sensu, to radykalnie inne podejście po prostu dlatego, że teraz musimy zapytać Gettera o podobne informacje.
Michael Ekoka
@Tamb: Sugerujesz, że kiedy musimy odpytać Getters, powinniśmy zmienić powyższy wzorzec i przenieść logikę wyboru do akcji proxy, która może uzyskać dostęp do Gettera i może skleić kilka małych głupich mutacji. Działa, ale nadal jest okrężny i nie rozwiązuje nieprzyjemnego zapachu, o którym mówię w mojej odpowiedzi, po prostu przenosi go w inne miejsce.
Michael Ekoka
Dokumentacja mówi, że aby użyć getterów, gdy trzeba obliczyć stan. Więc wydawało się poprawne do dnia dzisiejszego, są one podobne do obliczonych właściwości. Zastanów się, do czego zmierzasz, mówiąc, że akcja może skleić ze sobą mutacje. Dokumenty jasno mówią, że w działaniach należy umieścić logikę biznesową.
Tamb
15

Myślę, że odpowiedź TLDR jest taka, że ​​mutacje mają być synchroniczne / transakcyjne. Więc jeśli chcesz uruchomić wywołanie Ajax lub wykonać inny kod asynchroniczny, musisz to zrobić w Action, a następnie zatwierdzić mutację, aby ustawić nowy stan.

Noah Namey
źródło
1
To wygląda na podsumowanie dokumentacji; w którym nie ma nic złego. Jednak problem z tą odpowiedzią polega na tym, że to, co zapewnia, niekoniecznie jest prawdą. Możesz modyfikować stan wewnątrz mutacji podczas wywoływania funkcji asynchronicznej / AJAX, którą następnie można zmienić w całym wywołaniu zwrotnym. Myślę, że to właśnie powoduje tyle nieporozumień co do tego, dlaczego działania powinny być używane w najlepszych praktykach programistycznych podczas pracy z Vuex. Wiem, że z pewnością było to dla mnie źródłem nieporozumień, kiedy zacząłem pracować z Vuex.
Erutan409
8

Główne różnice między akcjami a mutacjami:

  1. Wewnątrz akcji można uruchamiać kod asynchroniczny, ale nie w mutacjach. Dlatego użyj akcji dla kodu asynchronicznego, w przeciwnym razie użyj mutacji.
  2. Wewnątrz akcji możesz uzyskać dostęp do getterów, stanu, mutacji (ich zatwierdzanie), akcji (ich wysyłanie) w mutacjach możesz uzyskać dostęp do stanu. Więc jeśli chcesz uzyskać dostęp tylko do stanu, użyj mutacji, w przeciwnym razie użyj akcji.
roli roli
źródło
5

Według docs

Działania są podobne do mutacji , z tą różnicą, że:

  • Zamiast mutacji stanu, działania popełnić mutacje.
  • Akcje mogą zawierać dowolne operacje asynchroniczne .

Rozważmy następujący fragment.

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

Programy obsługi akcji ( inkrementacja ) otrzymują obiekt kontekstu, który ujawnia ten sam zestaw metod / właściwości w instancji sklepu, dzięki czemu można wywołać context.commit w celu zatwierdzenia mutacji lub uzyskać dostęp do stanu i funkcji pobierających za pośrednictwem context.state i context.getters

Abdullah Khan
źródło
1
czy możliwe jest wywołanie funkcji „mutation”, metoda z komponentu vuejs?
Alberto Acuña
@ AlbertoAcuña Mam to samo pytanie, ponieważ kiedy próbuję to zrobić, wyskakuje błąd, że lokalna mutacja jest nieokreślona.
Rutwick Gangurde
5

Zastrzeżenie - dopiero zacząłem używać vuejs, więc to tylko ja ekstrapoluję założenia projektowe.

Debugowanie wehikułu czasu wykorzystuje migawki stanu i przedstawia oś czasu działań i mutacji. Teoretycznie moglibyśmy mieć tuż actionsobok nagrania ustawiaczy stanu i pobierających, aby synchronicznie opisać mutację. Ale wtedy:

  • Mielibyśmy nieczyste dane wejściowe (wyniki asynchroniczne), które spowodowałyby ustawianie i pobieranie. Byłoby to trudne do logicznego śledzenia i różne metody ustawiające asynchroniczne i pobierające mogą zaskakująco współdziałać. Może się tak zdarzyć w przypadku mutationstransakcji, ale wtedy możemy powiedzieć, że transakcja musi zostać ulepszona, a nie stan wyścigu w akcjach. Anonimowe mutacje wewnątrz akcji mogą łatwiej ujawnić tego rodzaju błędy, ponieważ programowanie asynchroniczne jest delikatne i trudne.
  • Dziennik transakcji byłby trudny do odczytania, ponieważ nie byłoby nazwy dla zmian stanu. Byłby bardziej podobny do kodu, a mniej angielski, bez logicznych grup mutacji.
  • Instrument rejestrujący jakąkolwiek mutację obiektu danych może być trudniejszy i mniej wydajny, w przeciwieństwie do sytuacji, w której istnieją synchronicznie zdefiniowane punkty różnic - przed i po wywołaniu funkcji mutacji. Nie jestem pewien, jak duży jest to problem.

Porównaj następujący dziennik transakcji z nazwanymi mutacjami.

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

Z dziennikiem transakcji, który nie ma nazwanych mutacji:

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

Mam nadzieję, że możesz ekstrapolować z tego przykładu potencjalną dodatkową złożoność asynchronii i anonimową mutację wewnątrz działań.

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

Teraz wyobraź sobie, że debugujemy aplikację i patrzymy na dzienniki mutacji devtool. Dla każdej zarejestrowanej mutacji program devtool będzie musiał przechwycić migawki stanu „przed” i „po”. Jednak asynchroniczne wywołanie zwrotne w powyższej przykładowej mutacji sprawia, że ​​jest to niemożliwe: callback nie jest jeszcze wywoływany, gdy mutacja zostanie zatwierdzona, a devtool nie ma możliwości, aby wiedzieć, kiedy wywołanie zwrotne zostanie faktycznie wywołane - jakakolwiek mutacja stanu wykonana w wywołaniu zwrotnym zasadniczo nie można go śledzić!

ubershmekel
źródło
4

Mutacje:

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

Działania:

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

W sposób Redux

Mutations are Reducers
Actions are Actions

Dlaczego obie?

Kiedy aplikacja się rozrasta, kodowanie i linie będą się zwiększać, tym razem musisz zająć się logiką w Akcjach, a nie w mutacjach, ponieważ mutacje są jedynym autorytetem do zmiany stanu, powinno być jak najbardziej czyste.

Gopinath Kaliappan
źródło
2

To też mnie zdezorientowało, więc zrobiłem proste demo.

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;

Po zbadaniu tego doszedłem do wniosku, że mutacje są konwencją skupioną tylko na zmianie danych, aby lepiej rozdzielić obawy i poprawić logowanie przed i po zaktualizowaniu danych. Natomiast działania są warstwą abstrakcji, która obsługuje logikę wyższego poziomu, a następnie odpowiednio wywołuje mutacje

Nathaniel Rink
źródło
0

1. z dokumentów :

Działania są podobne do mutacji, z tą różnicą, że:

  • Zamiast mutować państwo, działania powodują mutacje.
  • Akcje mogą zawierać dowolne operacje asynchroniczne.

Akcje mogą zawierać operacje asynchroniczne, ale mutacja nie.

2. Wzywamy mutację, możemy bezpośrednio zmienić stan. a także możemy w akcji zmieniać stany w następujący sposób:

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

Akcje są przeznaczone do obsługi większej liczby innych rzeczy, możemy tam zrobić wiele rzeczy (możemy użyć operacji asynchronicznych), a następnie zmienić stan przez mutację wysyłkową.

samolot
źródło
0

Ponieważ nie ma stanu bez mutacji! Po zatwierdzeniu - wykonywany jest kawałek logiki, który zmienia stan w przewidywalny sposób. Mutacje to jedyny sposób na ustawienie lub zmianę stanu (więc nie ma bezpośrednich zmian!), A ponadto - muszą być synchroniczne. To rozwiązanie napędza bardzo ważną funkcjonalność: mutacje logują się do devtools. A to zapewnia doskonałą czytelność i przewidywalność!

Jeszcze jedno - działania. Jak już powiedziano - działania powodują mutacje. Więc nie zmieniają sklepu i nie ma potrzeby, aby były synchroniczne. Ale mogą zarządzać dodatkowym elementem logiki asynchronicznej!

Sumit Patel
źródło
0

Może wydawać się niepotrzebne posiadanie dodatkowej warstwy actionstylko w celu wywołania mutations, na przykład:

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

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

Więc jeśli wywołanie actionspołączenia logout, dlaczego nie nazwać samą mutację?

Cała idea akcji polega na wywołaniu wielu mutacji z poziomu jednej akcji lub wykonaniu żądania Ajax lub dowolnej logiki asynchronicznej, jaką możesz sobie wyobrazić.

W końcu możemy mieć działania, które powodują wiele żądań sieciowych i ostatecznie wywołują wiele różnych mutacji.

Więc staramy się jak najwięcej rzeczy z naszego złożoności Vuex.Store(), jak to możliwe w naszych actionsi to opuszcza nasz mutations, statei gettersczystsze i proste i wpisuje się w tego rodzaju modułowość sprawia, że biblioteki jak Vue i reagować popularne.

Daniel
źródło
0

Zawodowo używam Vuex od około 3 lat i wydaje mi się, że odkryłem, jakie są podstawowe różnice między działaniami i mutacjami, jak możesz skorzystać z ich wspólnego używania i jak możesz utrudnić sobie życie, jeśli nie używaj go dobrze.

Głównym celem Vuex jest zaoferowanie nowego wzorca do kontroli zachowania aplikacji: reaktywności. Chodzi o to, aby odciążyć orkiestrację stanu aplikacji do wyspecjalizowanego obiektu: sklepu. W wygodny sposób zapewnia metody łączenia komponentów bezpośrednio z danymi sklepu, aby można było z nich korzystać w dogodnym dla siebie czasie. Pozwala to komponentom skupić się na ich zadaniu: definiowaniu szablonu, stylu i podstawowego zachowania komponentów do zaprezentowania użytkownikowi. W międzyczasie sklep obsługuje duże obciążenie danymi.

To jednak nie jedyna zaleta tego wzoru. Fakt, że magazyny są pojedynczym źródłem danych dla całej aplikacji, oferuje duży potencjał ponownego wykorzystania tych danych w wielu komponentach. Nie jest to pierwszy wzorzec, który próbuje rozwiązać ten problem komunikacji między komponentami, ale tam, gdzie świeci, jest to, że zmusza cię do zaimplementowania bardzo bezpiecznego zachowania w twojej aplikacji, zasadniczo zabraniając twoim komponentom modyfikowania stanu tych udostępnionych danych i zamiast tego zmusić go do używania „publicznych punktów końcowych”, aby poprosić o zmianę.

Podstawowa idea jest taka:

  • Sklep ma stan wewnętrzny, do którego komponenty nigdy nie powinny mieć bezpośredniego dostępu (mapState jest skutecznie zbanowany)
  • Sklep posiada mutacje, które są synchroniczną modyfikacją stanu wewnętrznego. Jedynym zadaniem mutacji jest modyfikacja stanu. Powinny być wywoływane tylko z akcji. Powinny być nazwane, aby opisać rzeczy, które wydarzyły się w stanie (ORDER_CANCELED, ORDER_CREATED). Niech będą krótkie i słodkie. Możesz przejść przez nie, używając rozszerzenia przeglądarki Vue Devtools (świetnie nadaje się również do debugowania!)
  • Sklep ma również akcje, które powinny być asynchroniczne lub zwrócić obietnicę. Są to akcje, które będą wywoływać Twoje komponenty, gdy będą chciały zmodyfikować stan aplikacji. Powinny one być nazwane ze zorientowanych biznesowych działań (czasowników, tj cancelOrder, createOrder). Tutaj sprawdzasz i wysyłasz swoje żądania. Każda akcja może wywołać różne zatwierdzenia w innym kroku, jeśli jest wymagana zmiana stanu.
  • Wreszcie, sklep ma metody pobierające, których używasz, aby ujawnić swój stan komponentom. Spodziewaj się, że będą one intensywnie używane w wielu komponentach w miarę rozwoju aplikacji. Vuex mocno buforuje pobierające dane, aby uniknąć niepotrzebnych cykli obliczeniowych (o ile nie dodajesz parametrów do swojego gettera - staraj się nie używać parametrów), więc nie wahaj się ich używać w szerokim zakresie. Tylko upewnij się, że podajesz nazwy, które opisują jak najbliżej, w jakim stanie jest obecnie aplikacja.

To powiedziawszy, magia zaczyna się, gdy zaczynamy projektować naszą aplikację w ten sposób. Na przykład:

  • Mamy komponent, który oferuje użytkownikowi listę zamówień z możliwością ich usunięcia
  • Komponenty zmapowały pobierający sklep (deletableOrders), który jest tablicą obiektów z identyfikatorami
  • Komponent ma przycisk na każdym wierszu zamówienia, a jego kliknięcie jest przypisane do akcji sklepu (deleteOrder), która przekazuje do niego obiekt zamówienia (który, jak pamiętamy, pochodzi z samej listy sklepu)
  • Akcja store deleteOrder wykonuje następujące czynności:
    • sprawdza poprawność usunięcia
    • przechowuje zamówienie do tymczasowego usunięcia
    • zatwierdza mutację ORDER_DELETED z zamówieniem
    • wysyła wywołanie API, aby faktycznie usunąć zamówienie (tak, PO zmodyfikowaniu stanu!)
    • czeka na zakończenie wywołania (stan jest już zaktualizowany) iw przypadku niepowodzenia wywołujemy mutację ORDER_DELETE_FAILED z zachowaną wcześniej kolejnością.
  • Mutacja ORDER_DELETED po prostu usunie dane zamówienie z listy usuwalnych zamówień (co zaktualizuje getter)
  • Mutacja ORDER_DELETE_FAILED po prostu odkłada ją z powrotem i modyfikuje do stanu, aby powiadomić o błędzie (inny składnik, powiadomienie o błędzie, śledziłby ten stan, aby wiedzieć, kiedy się wyświetlić)

W końcu mamy doświadczenie użytkownika, które jest uważane za „reaktywne”. Z punktu widzenia naszego użytkownika pozycja została natychmiast usunięta. W większości przypadków oczekujemy, że nasze punkty końcowe będą działać, więc jest to idealne rozwiązanie. Gdy to się nie powiedzie, nadal mamy kontrolę nad tym, jak będzie reagować nasza aplikacja , ponieważ udało nam się oddzielić obawy o stan naszej aplikacji front-end od rzeczywistych danych.

Pamiętaj, że nie zawsze potrzebujesz sklepu. Jeśli okaże się, że piszesz sklepy, które wyglądają tak:

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

Wydaje mi się, że używasz magazynu tylko jako magazynu danych i być może pomijasz jego aspekt reaktywności, nie pozwalając mu również przejąć kontroli nad zmiennymi, na które reaguje Twoja aplikacja. Zasadniczo możesz i prawdopodobnie powinieneś przenieść do sklepów niektóre wiersze kodu napisane w Twoich komponentach.

Alex
źródło