qolabNavigate back to the homepage

Základy knižnice Vuex

Viliam Žigo
February 21st, 2021 · 3 min read

Prečo sa zaujímať o Vuex?

Aby sme vedeli odpovedať na túto otázku musíme najprv vedieť čo Vuex vôbec je. Vuex je knižnica pre Vue.js aplikácie, ktorá sa zaoberá tzv. state manažmentom aplikácie. Zmysel má hlavne v stredných a väčších webových aplikáciach, ktoré sa skladajú z väčšieho počtu komponentov. Tieto komponenty potrebujú vo väčšine prípadov medzi sebou komunikovať. V čistom Vue.js je táto komunikácia vyriešená pomocou “props” (parent to child) a “events” (child to parent). No, ak máme veľa komponentov, tak prístup cez props a events sa stáva neprehľadný. To môže spôsobiť veľa opakujúceho sa kódu a chýb. Takže presne to, čo nechceme. Vuex ponúka riešenie tohto problému. Výhoda Vuex-u je jeho integrácia v priehliadačoch s oficiálnym Vue devtools rozšírením, kde si okrem počiatočného a aktuálneho stavu vieme pri debuggovaní doslova cestovať v čase a zvoliť si hociktorý zo stavov, ktoré aplikácia mala.

Ako to funguje?

Vuex predstavuje niečo ako centrálne úložisko pre premenné, tzv “store”. Predstavme si to ako globálny object alebo nejakú key-value databázu, kde máme uložený stav (“state”), svojich premenných, ku ktorým vieme pristupovať z celej aplikácie. Vysvetlíme si, ako so store pracovať na jednoduchom príklade. Predstavme si, že máme vytvoriť jednoduchú aplikáciu pre kníhkupectvo, v ktorej budú môcť filtrovať knihy pomocou nejakých parametrov. O knihách vieme nasledujúce informácie: názov, počet kusov, cena a stav (na sklade, vypredané, atď.). Tento príklad budeme demonštrovať na jednoduchej Vue.js aplikácii, ktorú si predom inicializujeme a nainštalujeme do nej knižnicu Vuex.

State

Najprv musíme v src/store/ vytvoriť súbor index.js, v ktorom nastavíme náš základný state, z ktorého budeme vychádzať. Majme pre začiatok 5 kníh. Knihy sa oplatí mať v store, pretože k nim budeme chcieť pravdepodobne pristupovať vo viacerých častiach aplikácie aj keď sa bude aplikácia rozširovať.

1// index.js
2import Vue from 'vue';
3import Vuex from 'vuex';
4
5Vue.use(Vuex);
6
7export default new Vuex.Store({
8 state: {
9 books: [
10 { id: 1, title: '', publisher: '', price: 12.99, status: 'in-stock'},
11 { id: 2, title: '', publisher: '', price: 6.99, status: 'sold-out'},
12 { id: 3, title: '', publisher: '', price: 25.99, status: 'in-stock'},
13 { id: 4, title: '', publisher: '', price: 17.99, status: 'comming-soon'},
14 { id: 5, title: '', publisher: '', price: 14.99, status: 'sold-out'},
15 ],
16 },
17});

To by sme mali. Informácie o knihách budeme zobrazovať v jednoduchej tabuľke s filtrom podla stavu. Taktiež vytvoríme možnosť pre administrátora, editovať stav knihy v tabuľke. Pre jednoduchosť použijeme pre templating knižnicu bootstrap-vue. Na to, aby sme mohli dáta zobraziť musíme tieto dáta získať zo store. Na pristupovanie k premenným v store sa využívajú “getters”. Tie nám dovoľujú jednoducho pristúpiť, ku každej premmennej v store. Predtým, ako nám takýto getter vráti obsah premennej, o ktorý ho žiadame, vieme tento obsah modifikovať a získať len časť, ktorú potrebujeme.

Getters

V aplikácii budeme potrebovať, getter pre získanie kníh podľa zadaného filtra. Ten by mohol vyzerať nasledovne:

1//index.js
2import Vue from 'vue';
3import Vuex from 'vuex';
4
5Vue.use(Vuex);
6
7export default new Vuex.Store({
8 state: {
9 books: [
10 { id: 1, title: '', publisher: '', price: 12.99, status: 'in-stock'},
11 ...,
12 { id: 5, title: '', publisher: '', price: 14.99, status: 'sold-out'},
13 ],
14 },
15 getters: {
16 getBooks: (state) => (filter) => {
17 if (filter) {
18 return state.books.filter((book) => book.status === filter);
19 }
20
21 return state.books;
22 }
23 },
24});

Pri getteroch sú dôležité dve veci. Vždy príjíma ako argument state a vždy musí niečo returnovať. Je to akási alternatíva ku computed properties vo Vue komponente avšak, k tejto metóde vieme pristupovať z celej aplikácie. Stačí, ak v komponente využijeme helper z vuex-u a pridáme potrebné premenné do časti computed.

1// import helperu
2import { mapGetters } from 'vuex';
3
4export default {
5 // ...
6
7 // v computed premenných využijeme helper,
8 // ktorý nám zavolá getter v store
9 computed: {
10 ...mapGetters(['getBooks']),
11 }
12};

Teraz už môžeme v aplikácii zobrazovať a filtrovať knihy podľa stavov. Ďalej by sme potrebovali knihy editovať. Keby sme nepoužívali vuex, tak by sme niečo takéto robili pravdepodobne pomocou využitia metódy. Alternatíva metód v store sú takzvané mutácie alebo mutations.

Mutations

Mutácie dovoľujú meniť (mutovať) stav premenných uložených v store. V našom prípade využijeme mutáciu na zmenu stavu knihy. Tá by mohla vyzerať nejako takto:

1// index.js
2import Vue from 'vue';
3import Vuex from 'vuex';
4
5Vue.use(Vuex);
6
7export default new Vuex.Store({
8 // ...
9
10 mutations: {
11 setStatus(state, payload) {
12 let book = state.books.find((book) => book.id === payload.id);
13 book.status = payload.status;
14 },
15 },
16});

Mutáciu rovnako ako getter a state pridáme v store. V našom prípade je to mutácia setStatus, ktorá prijíma dva argumenty. Prvý je vždy aktuálny state a druhý jepayload, ktorý je voliteľný. Využívame ho, ak chceme do našej mutácie posunúť nejaké dáta. My ako si ukážeme budeme predávať id knihy a nový status. Podľa tohto id nájdeme knihu a zmeníme jej status na nový. Opäť však musíme túto mutáPociu impotovať do komponentu, aby sme ju mohli zavolať.

1// import helperu
2import { mapMutation } from 'vuex';
3
4export default {
5 //...
6
7 methods: {
8 ...mapMutations(['setStatus']);
9
10 //...
11 }
12};

Po importe môžeme s touto mutáciou ďalej pracovať ako s obyčajnou metódou, pričom ako argument použijeme objekt našej knihy. Máme teda alternatívu aj k metódam, avšak nie je to také jednoduché, pretože mutácie nemôžu obsahovať asynchrónny kód a niekedy práve to potrebujeme. Napríklad, ak by sme zmenu stavu potrebovali odoslať do externého systému. Preto existujú takzvané actions alebo akcie.

Actions

Aj keď sú na prvý pohľad mutáciam podobné, existujú tri rozdiely medzi akciou a mutáciou:

  1. Actions môžu obsahovať asynchrónne operácie, mutations nie
  2. Actions miesto mutovania state-u commitujú mutácie
  3. Actions sa dispatchujú, mutations commitujú

Ukážeme to na našom príklade. Vytvoríme jednoduchú akciu update, ktorá nám commitne mutáciu setStatus. To by mohlo vyzerať nejako takto:

1import Vue from 'vue';
2import Vuex from 'vuex';
3
4Vue.use(Vuex);
5
6export default new Vuex.Store({
7 // ...
8
9 actions: {
10 update(context, payload) {
11 context.commit('setStatus', payload);
12 },
13 },
14});

Ako môžeme vidieť, akcia prijíma ako prvý argument celý context, v ktorom sa nachádza. My však nepotrebujeme celý context, takže môžeme z neho vybrať len metódu commit a naša akcia sa zmení na nasledujúci tvar.

1update({ commit }, payload) {
2 commit('setStatus', payload);
3},

Pre zaujímavosť, context obsahuje aj state, getters a všetko uvedené nižšie:

1{
2 state, // same as store.state, or local state if in modules
3 rootState, // same as store.state, only in modules
4 commit, // same as store.commit
5 dispatch, // same as store.dispatch
6 getters, // same as store.getters, or local getters if in modules
7 rootGetters // same as store.getters, only in modules
8}

Teraz potrebujeme ešte upraviť náš komponent aby namiesto mutácie dispatchol našu akciu. Na to môžeme opäť použiť helper mapActions, ktorý nám vuex ponúka. Všimnime si, že tento helper je opäť v methods. Jeho použitie je opäť veľmi jednoduché.

1import { mapActions } from 'vuex';
2
3export default {
4 // ...
5
6 methods: {
7 ...mapActions(['update']),
8
9 // ...
10 },
11};

Modules

Pri zložitejších aplikáciach sa môže stať, že potrebujete uložiť do state a mutovať veľké množstvo premenných. Aby v tom nevznikol neporiadok, dajú sa jednoduché časti rozdeliť do takzvaných modules. Každý modul môže obsahovať svoj vlastný state, gettery, mutácie a akcie. Avšak vždy budeme potrebovať aj takzvaný rootModule, v ktorom sme pracovali doteraz. Predstavte si to, že by sme do našej aplikácie chceli pridať okrem kníh aj časopisy a kalendáre. Funkcie by boli podobné, avšak štruktúra dát by sa môže byť odlišná. Aby sme nemali veľmi dlhé súbory a aby sa nám nepomiešali jednotlivé produkty, je rozumné rozdeliť tieto produkty do samostatných modulov. Následne tieto moduly zjednotíme v našom rootModule nasledovne.

1const store = new Vuex.Store({
2 modules: {
3 book: moduleBook,
4 journal: moduleJournal,
5 calendar: moduleCalendar
6 }
7})

Následne sa dostaneme k údajom takýmto spôsobom:

1store.state.journal // -> state v moduleJournal
2store.state.book.status // -> status knihy v moduleBook

Záver

Celú aplikáciu môžete nájsť v priloženom sandboxe:

Užitočné odkazy

  • Oficiálna dokumentácia Vuex
© 2021 qolabhello@qolab.euqolab.eu
Link to $https://github.com/qolabLink to $https://www.linkedin.com/company/qolab-europeLink to $https://facebook.com/qolab.sro/