As aplicações front-end, especialmente as mais complexas, têm de processar uma grande quantidade de dados. Os programadores introduzem vários padrões de conceção para tornar os seus projectos legíveis e fáceis de manter. Na maioria dos cenários comuns de lidar com um MVC, queremos separar os dados das partes visuais da aplicação.
É por essa razão que loja tornou-se tão útil. Cabe-lhe a si decidir se quer utilizar React + Redux ou Vue + Vuex - o objetivo principal é o mesmo, nomeadamente manter os seus dados estruturados, acessíveis e seguros ao mesmo tempo.
Neste artigo, vou mostrar-lhe alguns exemplos de como manter a sua loja Vuex limpa e eficiente.
Antes de começarmos, vamos supor que:
- tem alguma experiência com a tecnologia moderna JavaScript,
- sabe basicamente o que é o Vue e como utilizá-lo adereços, calculado, etc..,
- está familiarizado com o Vuex (acções, mutações, etc.) e pretende melhorar as suas aplicações.
Vuexcomo a maior parte dos principais Projectos Vueestá bastante bem documentado e pode encontrar muitos hacks úteis na documentação oficial. Extraímos algumas informações essenciais para si.
Uma implementação básica do armazém Vuex tem o seguinte aspeto:
// main.js
import Vue from 'vue'
import Vuex from 'vuex'
import App from "./App";
Vue.use(Vuex)
const store = new Vuex.Store((
estado: (
data: null;
),
acções: (
someAction: (( commit ), data) (
commit("SOME_MUTATION", data);
)
),
mutações: (
SOME_MUTATION (estado, dados) (
estado.dados = dados;
)
))
));
novo Vue((
el: "#app",
render: h => h(App),
store
));
Normalmente, quando a aplicação se torna maior, é necessário aplicar o encaminhamento, algumas diretivas globais, plugins, etc. Isso faz com que o principal.js muito mais longo e mais difícil de ler. É uma boa prática manter o armazenamento num ficheiro externo, como aqui:
// store.js
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex);
const state = (
data: null;
);
const actions = (
someAction: (( commit ), data) (
commit("SOME_MUTATION", data);
)
);
const mutations = (
ALGUMA_MUTAÇÃO (estado, dados) (
estado.dados = dados;
)
);
export default new Vuex.Store((
estado,
acções,
mutações
));
1. Módulos
O que é que se deve fazer quando o loja.js fica enorme e difícil de trabalhar? Na verdade, existe uma funcionalidade muito fixe do Vuex - módulos. São dedicados a dividir os seus dados em ficheiros separados.
Imagine que trabalha numa aplicação empresarial, na qual tem poucos domínios de dados, por exemplo:
- utilizador (gerir todas as autorizações e permissões),
- parâmetros de itinerário (gerir parâmetros globais antes dos pedidos à API),
- vendas (para o seu componente SalesMegaChart visível num contexto mensal/trimestral/anual),
- encomendas (visível depois de clicar na barra SalesMegaChart).
...e talvez mais alguns. Agora tem sérias razões para introduzir alguma modularidade na sua loja.
Em primeiro lugar, mova o loja.js para um ficheiro loja/ e mudar-lhe o nome index.js. Opcionalmente, se pretender manter tudo agrupado em módulos, remova Estado, acções e mutações do ficheiro principal.
// store/index.js
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex);
export default new Vuex.Store((
módulos: (
// os módulos vão para aqui
)
));
Em seguida, ao lado do arquivo `store/index.js`, crie o primeiro módulo - `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 (erro) (
commit("LOGIN_ERROR", erro);
)
)
);
const mutations = (
SAVE_USER (estado, utilizador) (
estado.utilizador = utilizador;
),
LOGIN_SUCCESS (estado) (
state.loggedIn = true;
),
LOGIN_ERROR (estado, erro) (
estado.loginError = erro;
state.loggedIn = false;
)
);
export const user (
estado,
acções,
mutações
)
E agora, carregue o módulo pronto no arquivo principal `store/index.js`:
import Vue from 'vue'
import Vuex from 'vuex'
import ( user ) from './user';
Vue.use(Vuex);
export default new Vuex.Store((
módulos: (
utilizador
)
));
Parabéns! Agora tem uma implementação de loja com um aspeto muito agradável. Também é possível aceder aos dados a partir do componente (por exemplo, UserProfile.vue) assim:
<template>
<div class="user-profile">
<h2>(( nome do utilizador ))!</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. Espaços de nomes
Agora que já sabe como utilizar os módulos, deve também familiarizar-se com as funções do Vuex namespacing. No passo anterior, criámos o ficheiro store/user.js com o ficheiro utilizador módulo.
A estrutura de dados definida no utilizador.js é acessível a partir dos componentes, mas é possível detetar que todos os ficheiros utilizador os dados vão diretamente para o Estado contexto, como aqui:
computado: mapState((
utilizador: estado => estado.utilizador
// utilizador: 'user' <-- forma alternativa
))
Quando definir mais módulos, é provável que se confunda sobre que objeto provém de que módulo. Nesse caso, deve utilizar módulos com espaço de nome e defini-los desta forma:
export const user (
namespaced: true, // <-- namespacing!
estado,
acções,
mutações
)
A partir de agora, todos os seus utilizador dados (Estado variável de store/user.js ) serão tratados no âmbito do ficheiro estado.utilizador referência:
computado: mapState((
utilizador: estado => estado.utilizador.utilizador
// utilizador: 'user/user' <-- forma alternativa
))
Algumas etapas depois, é possível obter para o componente algo como isto:
import ( mapActions ) from 'Vuex';
exportar por defeito (
nome: 'Dashboard',
computed: mapState((
vendas: 'vendas/dados',
encomendas: 'encomendas/dados',
sortBy: 'orders/sortBy',
loggedIn: 'user/loggedIn'
)),
métodos: mapActions((
logout: 'user/logout',
loadSales: 'sales/load',
loadOrders: 'orders/load'
)),
created() (
se (this.loggedIn) (
loadSales();
loadOrders();
)
)
)
Bravo! Tão fresco, tão limpo... Mas não se preocupe, a refacção nunca acaba. Pronto para os próximos passos?
3. Comunicação entre módulos
No primeiro passo, mostrei algumas acções no utilizador módulo:
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 (erro) (
commit("LOGIN_ERROR", erro);
)
)
);
Em caso de falha, estamos a adicionar o erro de início de sessão à nossa loja - o que se segue?
Aqui temos algumas opções e a escolha depende da opção que melhor se adapta às suas necessidades. A forma mais simples utiliza o v-if graças à qual pode ser apresentada uma mensagem de erro se houver um erro na sua loja.
<template>
<div class="dashboard">
<!-- dashboard component template -->
<div
v-if="error"
class="error-message"
> (( mensagem de erro )) </div>
</div>
</template>
<script> import ( mapActions ) from 'Vuex';
export default (
name: 'Dashboard',
computed: mapState((
error: "user/loginError"
))
)
</script>
Mais uma vez, imagine que tem muitos módulos e cada um deles try/catch gera um novo erro na sua loja. Obviamente, vai abusar da regra DRY desta forma.
Como é que pode tornar os seus processos de tratamento de erros mais genéricos?
Vamos definir o comum e introduzir aí alguma lógica que seria utilizada globalmente.
// store/common.js
const state = (
errors: []
);
const actions = (
erro: (
root: true,
handler(( commit ), error) (
commit("ERROR", erro);
)
)
),
const mutations = (
ERRO (estado, erro) (
/* desta forma vamos ter o erro mais recente no topo da lista */
estado.erros = [erro, ...estado.erros];
))
);
export const common (
namespaced: true,
state,
mutações
)
Agora, podemos adaptar o utilizador (e outros módulos também):
try (
// alguma ação
)catch (error) (
commit("common/ERROR", error, ( root: true ));
)
ou de uma forma mais elegante, utilizando a nossa ação global:
try (
// alguma ação
) catch (error) (
dispatch("error", error);
)
Esta sintaxe de comprometer-se e despacho As chamadas parecem ser auto-explicativas, mas pode ler mais sobre estes truques aqui.
Quando tiver todos os erros num único local, pode facilmente carregá-los para o seu Painel de controlo componente:
computado: mapState((
errors: 'common/errors'
)),
watch: (
/* isto será invocado após cada mutação "common/ERROR", onde apenas adicionamos novos erros à loja, um a um */
errors() (
this.showErrorMessage(this.errors[0]);
)
)
O exemplo anterior com o comum O tratamento de erros por módulos já é uma solução eficiente, mas pode ir ainda mais longe.
Como pode ver, estamos a observar as alterações no comum/erros na loja. Em casos como este, quando é necessário determinar alguma ação sobre uma mutação específica, pode utilizar Vuex plugins ou mesmo componentes de ordem superior (HOC).
Falarei sobre os plugins e HOCs no próximo artigo. Entretanto, obrigado por ler este artigo, espero que tenha gostado dos exemplos que preparámos.
Fique atento e continue a programar!
Ler mais:
– Como melhorar as aplicações Vue.js? Algumas dicas práticas
– GraphQL: lições aprendidas em produção
– Shopify, Spree ou Solidus? Veja porque é que a Ruby on Rails o pode ajudar a desenvolver o seu comércio eletrónico