Aplikacje frontendowe, zwłaszcza te bardziej złożone, muszą przetwarzać wiele danych. Programiści wprowadzają różne wzorce projektowe, aby ich projekty były czytelne i łatwe w utrzymaniu. W większości typowych scenariuszy pracy z MVC, chcemy oddzielić dane od wizualnych części aplikacji.
To jest powód, dla którego sklep stało się tak przydatne. To od Ciebie zależy, czy użyjesz React + Redux czy Vue + Vuex - główny cel jest taki sam, a mianowicie jednoczesne utrzymywanie struktury, dostępności i bezpieczeństwa danych.
W tym artykule pokażę kilka przykładów, jak utrzymać sklep Vuex w czystości i wydajności.
Zanim zaczniemy, załóżmy, że:
- masz jakieś doświadczenie z nowoczesnymi JavaScript,
- Zasadniczo wiesz, czym jest Vue i jak go używać rekwizyty, obliczone, itd,
- jesteś zaznajomiony z Vuex (działania, mutacje, itp.) i chcą ulepszyć swoje aplikacje.
Vuexpodobnie jak większość rdzeni Projekty Vuejest dość dobrze udokumentowany i można znaleźć wiele przydatnych hacków w oficjalnych dokumentach. Wyciągnęliśmy z niej kilka istotnych informacji.
Podstawowa implementacja sklepu Vuex wygląda następująco:
// main.js
import Vue z 'vue'
import Vuex z 'vuex'
import App from "./App";
Vue.use(Vuex)
const store = new Vuex.Store((
state: (
data: null;
),
actions: (
someAction: (( commit ), data) (
commit("SOME_MUTATION", data);
)
),
mutacje: (
SOME_MUTATION (state, data) (
state.data = data;
)
))
));
new Vue((
el: "#app",
render: h => h(App),
store
));
Zwykle, gdy aplikacja staje się większa, trzeba zastosować routing, niektóre globalne dyrektywy, wtyczki itp. To sprawia, że main.js
plik jest znacznie dłuższy i trudniejszy do odczytania. Dobrą praktyką jest przechowywanie sklepu w zewnętrznym pliku, takim jak tutaj:
// store.js
import Vue z 'vue'
import Vuex z 'vuex'
Vue.use(Vuex);
const state = (
data: null;
);
const actions = (
someAction: (( commit ), data) (
commit("SOME_MUTATION", data);
)
);
const mutations = (
SOME_MUTATION (state, data) (
state.data = data;
)
);
export default new Vuex.Store((
state,
actions,
mutacje
));
1. Moduły
Co należy zrobić, gdy store.js
plik staje się ogromny i trudny w obróbce? W rzeczywistości istnieje naprawdę fajna funkcja Vuex - moduły. Są one przeznaczone do dzielenia danych na osobne pliki.
Wyobraź sobie, że pracujesz nad jakąś aplikacją korporacyjną, w której masz na przykład kilka domen danych:
- użytkownik (zarządzanie wszystkimi autoryzacjami i uprawnieniami),
- parametry trasy (zarządzanie parametrami globalnymi przed żądaniami do API),
- sales (dla komponentu SalesMegaChart widocznego w kontekście miesięcznym/kwartalnym/rocznym),
- zamówień (widoczne po kliknięciu paska SalesMegaChart).
...i może jeszcze kilka innych. Teraz masz poważne powody, aby wprowadzić modułowość w swoim sklepie.
Przede wszystkim należy przenieść store.js
do nowo utworzonego pliku sklep/
i zmienić jego nazwę index.js
. Opcjonalnie, jeśli chcesz zachować wszystko spakowane w modułach, usuń stan, działania i mutacje z głównego pliku.
// store/index.js
import Vue z 'vue'
import Vuex from 'vuex'
Vue.use(Vuex);
export default new Vuex.Store((
modules: (
// moduły trafią tutaj
)
));
Następnie, obok pliku `store/index.js`, utwórz pierwszy moduł - `store/user.js`.
import ApiService from '../services/api.service';
const state = (
loggedIn: false,
loginError: null,
user: null
);
const actions = (
login: async (( commit ), data) (
try (
const response = await ApiService.post('/login', data);
const ( user ) = response.data;
COMMIT("SAVE_USER", user);
COMMIT("LOGIN_SUCCESS");
) catch (error) (
commit("LOGIN_ERROR", błąd);
)
)
);
const mutations = (
SAVE_USER (state, user) (
state.user = user;
),
LOGIN_SUCCESS (state) (
state.loggedIn = true;
),
LOGIN_ERROR (state, error) (
state.loginError = error;
state.loggedIn = false;
)
);
export const user (
state,
actions,
mutacje
)
A teraz załaduj gotowy moduł do głównego pliku `store/index.js`:
import Vue from 'vue'
import Vuex from 'vuex'
import ( user ) from './user';
Vue.use(Vuex);
export default new Vuex.Store((
modules: (
użytkownik
)
));
Gratulacje! Teraz masz naprawdę ładnie wyglądającą implementację sklepu. Możesz również uzyskać dostęp do danych z komponentu (np, UserProfile.vue
) w ten sposób:
<template>
<div class="user-profile">
<h2>(( user.name ))!</h2>
<!-- component template goes here -->
</div>
</template>
<script> import ( mapActions ) from 'Vuex';
export default (
name: 'UserProfile',
computed: mapState((
user: state => state.user
// user: 'user' <-- alternative syntax
))
)
</script>
2. Przestrzenie nazw
Teraz, gdy już wiesz, jak korzystać z modułów, powinieneś również zapoznać się z Vuex przestrzeń nazw. W poprzednim kroku utworzyliśmy plik store/user.js
plik z rozszerzeniem użytkownik moduł.
Struktura danych zdefiniowana w user.js
plik jest dostępny z komponentów, ale można zauważyć, że wszystkie użytkownik dane trafiają bezpośrednio do globalnego stan
kontekst, jak tutaj:
computed: mapState((
user: state => state.user
// user: 'user' <-- alternatywny sposób
))
Gdy zdefiniujesz więcej modułów, prawdopodobnie nie będziesz wiedział, który obiekt pochodzi z którego modułu. Wtedy powinieneś używać modułów z przestrzenią nazw i definiować je w ten sposób:
export const user (
namespaced: true, // <-- namespacing!
state,
actions,
mutacje
)
Od teraz wszystkie twoje użytkownik dane (stan
zmienna z store/user.js
plik) będzie obsługiwany w ramach state.user
odniesienie:
computed: mapState((
user: state => state.user.user
// user: 'user/user' <-- alternatywny sposób
))
Kilka kroków później można uzyskać dla komponentu coś takiego:
import ( mapActions ) from 'Vuex';
export default (
name: 'Dashboard',
computed: mapState((
sales: 'sales/data',
orders: 'orders/data',
sortBy: 'orders/sortBy',
loggedIn: 'user/loggedIn'
)),
methods: mapActions((
logout: 'user/logout',
loadSales: "sales/load",
loadOrders: 'orders/load'
)),
created() (
if (this.loggedIn) (
loadSales();
loadOrders();
)
)
)
Brawo! Tak świeżo, tak czysto... Ale nie martw się, refaktoryzacja nigdy się nie kończy. Gotowy na kolejne kroki?
3. Komunikacja między modułami
W pierwszym kroku pokazałem trochę akcji w użytkownik moduł:
const actions = (
login: async (( commit ), data) (
try (
const response = await ApiService.post('/login', data);
const ( user ) = response.data;
COMMIT("SAVE_USER", user);
COMMIT("LOGIN_SUCCESS");
) catch (error) (
commit("LOGIN_ERROR", błąd);
)
)
);
W przypadku niepowodzenia dodajemy błąd logowania do naszego sklepu - co dalej?
Mamy tutaj kilka opcji, a wybór zależy od tego, która z nich bardziej odpowiada naszym potrzebom. Najprostszym sposobem jest użycie v-if
dzięki której można wyświetlić komunikat o błędzie w przypadku wystąpienia błędu w sklepie.
<template>
<div class="dashboard">
<!-- dashboard component template -->
<div
v-if="error"
class="error-message"
> (( error.message )) </div>
</div>
</template>
<script> import ( mapActions ) from 'Vuex';
export default (
name: 'Dashboard',
computed: mapState((
error: "user/loginError"
))
)
</script>
Ponownie, wyobraź sobie, że masz wiele modułów i każdy z nich try/catch
generuje nowy błąd w sklepie. Oczywiście w ten sposób nadużywasz reguły DRY.
Jak sprawić, by procesy obsługi błędów były bardziej ogólne?
Zdefiniujmy wspólny i umieścić tam logikę, która będzie używana globalnie.
// store/common.js
const state = (
errors: []
);
const actions = (
error: (
root: true,
handler(( commit ), error) (
commit("ERROR", błąd);
)
)
),
const mutations = (
ERROR (state, error) (
/* w ten sposób będziemy mieć najnowszy błąd na górze listy */
state.errors = [error, ...state.errors];
))
);
export const common (
namespaced: true,
state,
mutacje
)
Teraz możemy dostosować użytkownik (a także innych modułów):
try (
// jakaś akcja
)catch (error) (
commit("common/ERROR", error, ( root: true ));
)
lub w bardziej elegancki sposób, używając naszej globalnej akcji:
try (
// jakaś akcja
) catch (error) (
dispatch("error", error);
)
Ta składnia zobowiązanie
i wysyłka
Połączenia wydają się oczywiste, ale możesz przeczytać więcej o tych sztuczkach tutaj.
Gdy wszystkie błędy znajdują się w jednym miejscu, można je łatwo załadować do aplikacji Pulpit nawigacyjny
składnik:
computed: mapState((
errors: 'common/errors'
)),
watch: (
/* będzie to wywoływane po każdej mutacji "common/ERROR", gdzie dodajemy tylko nowe błędy do magazynu, jeden po drugim */
errors() (
this.showErrorMessage(this.errors[0]);
)
)
Poprzedni przykład z wspólny Moduł obsługi błędów jest już wydajnym rozwiązaniem, ale można pójść jeszcze dalej.
Jak widać, obserwujemy zmiany na stronie wspólne/błędy
w sklepie. W takich przypadkach, gdy trzeba określić jakieś działanie na konkretnej mutacji, można użyć Wtyczki Vuex lub nawet komponenty wyższego rzędu (HOC).
Wtyczki i HOC omówię w następnym artykule. Tymczasem dziękuję za przeczytanie tego wpisu, mam nadzieję, że spodobały Ci się przygotowane przez nas przykłady.
Bądź na bieżąco i koduj dalej!
Czytaj więcej:
– Jak ulepszyć aplikacje Vue.js? Kilka praktycznych wskazówek
– GraphQL: wnioski wyciągnięte z produkcji
– Shopify, Spree czy Solidus? Sprawdź, dlaczego Ruby on Rails może pomóc Ci rozwinąć Twój e-commerce