Las aplicaciones frontales, especialmente las más complejas, tienen que procesar muchos datos. Los programadores introducen varios patrones de diseño para hacer sus proyectos legibles y mantenibles. En la mayoría de los escenarios comunes de tratar con un MVC, queremos separar los datos de las partes visuales de la aplicación.
Esa es la razón por la que tienda ha llegado a ser tan útil. Depende de usted si utiliza React + Redux o Vue + Vuex - el objetivo principal es el mismo, a saber mantener sus datos estructurados, accesibles y seguros al mismo tiempo.
En este artículo, voy a mostrarte algunos ejemplos de cómo mantener tu tienda Vuex limpia y eficiente.
Antes de empezar, vamos a suponer que:
- tiene alguna experiencia con JavaScript,
- básicamente sabes qué es Vue y cómo se utiliza atrezzo, computada, etc,
- está familiarizado con Vuex (acciones, mutaciones, etc.) y quieres mejorar tus aplicaciones.
Vuexcomo la mayoría de los Proyectos Vueestá bastante bien documentado y puedes encontrar muchos trucos útiles en la documentación oficial. Hemos extraído alguna información esencial para ti.
Una implementación básica del almacén Vuex tiene este aspecto:
// main.js
import Vue from 'vue'
import Vuex from 'vuex'
import App from "./App";
Vue.use(Vuex)
const store = nuevo Vuex.Store((
estado: (
datos: null;
),
acciones: (
someAction: (( commit ), datos) (
commit("SOME_MUTATION", datos);
)
),
mutaciones: (
SOME_MUTATION (estado, datos) (
estado.datos = datos;
)
))
));
nuevo Vue((
el: "#app",
render: h => h(App),
almacenar
));
Normalmente, cuando tu aplicación se hace más grande, tienes que aplicar enrutamiento, algunas directivas globales, plugins, etc. Esto hace que la main.js
mucho más largo y difícil de leer. Es una buena práctica para mantener la tienda en un archivo externo, como aquí:
// store.js
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex);
const state = (
datos: null;
);
const actions = (
someAction: (( commit ), datos) (
commit("SOME_MUTATION", datos);
)
);
const mutaciones = (
SOME_MUTATION (estado, datos) (
estado.datos = datos;
)
);
exportar por defecto new Vuex.Store((
estado,
acciones,
mutaciones
));
1. Módulos
¿Qué debe hacer cuando el store.js
se vuelve enorme y difícil de trabajar? En realidad, hay una función realmente genial del Vuex - módulos. Se dedican a dividir tus datos en archivos separados.
Imagina que trabajas en alguna aplicación corporativa, en la que tienes pocos dominios de datos, por ejemplo:
- usuario (gestionar todas las autorizaciones y permisos),
- parámetros de ruta (gestionar los parámetros globales antes de las peticiones a la API),
- ventas (para su componente SalesMegaChart visible en un contexto mensual/trimestral/año),
- pedidos (visible tras hacer clic en la barra SalesMegaChart).
...y quizá algunas más. Ahora tienes serias razones para introducir algo de modularidad en tu tienda.
En primer lugar, mueva el store.js
a un archivo tienda/
y cámbiale el nombre index.js
. Opcionalmente, si desea mantener todo empaquetado en módulos, elimine estado, acciones y mutaciones del archivo principal.
// store/index.js
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex);
exportar por defecto new Vuex.Store((
módulos: (
// los módulos irán aquí
)
));
A continuación, junto al archivo `store/index.js`, crea el primer módulo - `store/user.js`.
import ApiService from '../services/api.service';
const state = (
loggedIn: false,
loginError: null,
usuario: null
);
const actions = (
login: async (( commit ), data) (
try (
const response = await ApiService.post('/login', data);
const ( usuario ) = response.data;
COMMIT("GUARDAR_USUARIO", usuario);
COMMIT("LOGIN_SUCCESS");
) catch (error) (
commit("LOGIN_ERROR", error);
)
)
);
const mutations = (
SAVE_USER (estado, usuario) (
estado.usuario = usuario;
),
LOGIN_SUCCESS (estado) (
state.logIn = true;
),
LOGIN_ERROR (estado, error) (
state.loginError = error;
state.loggedIn = false;
)
);
export const usuario (
estado,
acciones,
mutaciones
)
Y ahora, carga el módulo listo en el archivo principal `store/index.js`:
import Vue from 'vue'
import Vuex from 'vuex'
import ( user ) from './user';
Vue.use(Vuex);
exportar por defecto new Vuex.Store((
módulos: (
usuario
)
));
Enhorabuena. Ahora tienes una implementación de tienda realmente bonita. También puede acceder a los datos desde el componente (por ejemplo, UserProfile.vue
) así:
<template>
<div class="user-profile">
<h2>¡(( nombre.usuario ))!</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. Espacios de nombres
Ahora que ya sabes cómo utilizar los módulos, también deberías familiarizarte con el Vuex namespacing. En el paso anterior, creamos el tienda/usuario.js
con el archivo usuario módulo.
La estructura de datos definida en el usuario.js
es accesible desde los componentes, pero se puede observar que todos los usuario los datos van directamente al estado
contexto, como aquí:
calculado: mapState((
usuario: estado => estado.usuario
// usuario: 'usuario' <-- forma alternativa
))
Cuando definas más módulos, probablemente te confundas sobre qué objeto proviene de qué módulo. Entonces deberías usar módulos namespaced y definirlos de esta manera:
export const usuario (
¡namespaced: true, // <-- namespacing!
estado,
acciones,
mutaciones
)
A partir de ahora, todos tus usuario datos (estado
variable de tienda/usuario.js
) se gestionarán bajo el estado.usuario
referencia:
calculado: mapState((
usuario: estado => estado.usuario.usuario
// usuario: 'usuario/usuario' <-- forma alternativa
))
Unos pasos después, puedes conseguir para el componente algo como esto:
import ( mapActions ) from 'Vuex';
export default (
nombre: 'Dashboard',
calculado: mapState((
ventas: 'ventas/datos
pedidos: 'pedidos/datos',
sortBy: 'pedidos/sortBy',
conectado: 'usuario/conectado'
)),
métodos: mapActions((
cerrar sesión: 'user/logout',
loadSales: 'ventas/cargar',
cargarPedidos: 'pedidos/cargar'
)),
creado() (
if (this.loggedIn) (
loadVentas();
cargarPedidos();
)
)
)
¡Bravo! Tan fresco, tan limpio... Pero no te preocupes, la refactorización nunca termina. ¿Listo para los siguientes pasos?
3. Comunicación entre módulos
En el primer paso, mostré alguna acción en el usuario módulo:
const actions = (
login: async (( commit ), data) (
try (
const response = await ApiService.post('/login', data);
const ( usuario ) = response.data;
COMMIT("GUARDAR_USUARIO", usuario);
COMMIT("LOGIN_SUCCESS");
) catch (error) (
commit("LOGIN_ERROR", error);
)
)
);
En caso de fallo, añadimos el error de inicio de sesión a nuestra tienda, ¿qué sigue?
Aquí tenemos varias opciones y la elección depende de la que mejor se adapte a sus necesidades. La forma más sencilla utiliza el v-if
gracias a la cual se puede mostrar un mensaje de error si se produce un error en la tienda.
<template>
<div class="dashboard">
<!-- dashboard component template -->
<div
v-if="error"
class="error-message"
> (( mensaje.error )) </div>
</div>
</template>
<script> import ( mapActions ) from 'Vuex';
export default (
name: 'Dashboard',
computed: mapState((
error: "user/loginError"
))
)
</script>
De nuevo, imagine que tiene muchos módulos y que cada uno de ellos try/catch
genera un nuevo error en tu tienda. Obviamente, vas a abusar de la regla DRY de esta manera.
¿Cómo puede hacer que sus procesos de tratamiento de errores sean más genéricos?
Definamos el común y poner un poco de lógica que se utilizaría a nivel mundial.
// store/common.js
const state = (
errores []
);
const actions = (
error: (
raíz: true,
handler(( commit ), error) (
commit("ERROR", error);
)
)
),
const mutaciones = (
ERROR (estado, error) (
/* de esta forma tendremos el error más reciente al principio de la lista */
estado.errores = [error, ...estado.errores];
))
);
export const common (
namespaced: true,
estado,
mutaciones
)
Ahora, podemos adaptar el usuario (y también otros módulos):
prueba (
// alguna acción
)catch (error) (
commit("common/ERROR", error, ( root: true ));
)
o de forma más elegante, usando nuestra acción global:
try (
// alguna acción
) catch (error) (
dispatch("error", error);
)
Esta sintaxis de escriba a
y enviar
parece evidente, pero puedes leer más sobre estos trucos aquí.
Cuando tenga todos los errores en un solo lugar, podrá cargarlos fácilmente en su Cuadro de mandos
componente:
calculado: mapState((
errores 'common/errors'
)),
vigilar: (
/* esto se invocará después de cada mutación "common/ERROR", donde sólo añadimos nuevos errores al almacén, uno a uno */
errores() (
this.showErrorMessage(this.errores[0]);
)
)
El ejemplo anterior con el común ya es una solución eficaz, pero se puede ir aún más lejos.
Como puede ver, estamos observando cambios en el común/errores
en el almacén. En casos como éste, cuando se necesita determinar alguna acción sobre una mutación concreta, se puede utilizar Plugins Vuex o incluso Componentes de Orden Superior (HOC).
Hablaré de los plugins y HOCs en el próximo artículo. Mientras tanto, gracias por leer esta entrada, espero que hayas disfrutado de los ejemplos que hemos preparado.
Permanezca atento y siga codificando.
Más información:
– ¿Cómo mejorar las aplicaciones Vue.js? Algunos consejos prácticos
– GraphQL: lecciones aprendidas en producción
– ¿Shopify, Spree o Solidus? Compruebe por qué Ruby on Rails puede ayudarle a desarrollar su comercio electrónico