Vue.js
Vuex
Buscar..
Introducción
Vuex es una biblioteca de patrones de administración de estado para aplicaciones Vue.js. Sirve como un almacén centralizado para todos los componentes de una aplicación, con reglas que garantizan que el estado solo se puede mutar de manera predecible. También se integra con la extensión de herramientas de desarrollo oficial de Vue para proporcionar funciones avanzadas como la depuración de viajes en tiempo de configuración cero y la exportación / importación de instantáneas de estado.
¿Qué es Vuex?
Vuex es un complemento oficial para Vue.js que ofrece un almacén de datos centralizado para usar dentro de su aplicación. Está muy influido por la arquitectura de la aplicación Flux, que presenta un flujo de datos unidireccional que conduce a un diseño y un razonamiento más simples.
Dentro de una aplicación Vuex, el almacén de datos contiene todo el estado de la aplicación compartida . Este estado se altera por las mutaciones que se realizan en respuesta a una acción que invoca un evento de mutación a través del despachador .
Un ejemplo del flujo de datos en una aplicación Vuex se describe en el diagrama a continuación. Diagrama utilizado bajo la licencia MIT , originalmente del repositorio oficial de Vuex GitHub .
Los componentes individuales de la aplicación Vue.js pueden acceder al objeto de la tienda para recuperar datos a través de captadores , que son funciones puras que devuelven una copia de solo lectura de los datos deseados.
Los componentes pueden tener acciones que son funciones que realizan cambios en la propia copia de los datos del componente, luego usan el despachador para enviar un evento de mutación. Este evento es manejado por el almacén de datos que actualiza el estado según sea necesario.
Los cambios se reflejan automáticamente en toda la aplicación, ya que todos los componentes están vinculados de forma reactiva a la tienda a través de sus captadores.
Un ejemplo que ilustra el uso de vuex en un proyecto de vue.
const state = {
lastClickTime: null
}
const mutations = {
updateLastClickTime: (state, payload) => {
state.lastClickTime = payload
}
}
const getters = {
getLastClickTime: state => {
return new Date(state.lastClickTime)
}
}
const actions = {
syncUpdateTime: ({ commit }, payload) => {
commit("updateLastClickTime", payload)
},
asyncUpdateTime: ({ commit }, payload) => {
setTimeout(() => {
commit("updateLastClickTime", payload)
}, Math.random() * 5000)
}
}
const store = new Vuex.Store({
state,
getters,
mutations,
actions
})
const { mapActions, mapGetters } = Vuex;
// Vue
const vm = new Vue({
el: '#container',
store,
computed: {
...mapGetters([
'getLastClickTime'
])
},
methods: {
...mapActions([
'syncUpdateTime',
'asyncUpdateTime'
]),
updateTimeSyncTest () {
this.syncUpdateTime(Date.now())
},
updateTimeAsyncTest () {
this.asyncUpdateTime(Date.now())
}
}
})
Y la plantilla HTML para el mismo:
<div id="container">
<p>{{ getLastClickTime || "No time selected yet" }}</p>
<button @click="updateTimeSyncTest">Sync Action test</button>
<button @click="updateTimeAsyncTest">Async Action test</button>
</div>
Aquí el estado contiene la propiedad lastClickTime inicializada como nula. Esta configuración de valores predeterminados es importante para mantener las propiedades reactivas . Las propiedades no mencionadas en el estado estarán disponibles, pero los cambios que se realicen posteriormente no serán accesibles mediante el uso de captadores.
El captador utilizado proporciona una propiedad calculada que se actualizará cada vez que una mutación actualice el valor de la propiedad estatal.
Solo se permite que las mutaciones cambien el estado y sus propiedades, dicho esto, solo lo hace de forma síncrona .
Se puede usar una Acción en caso de actualizaciones asíncronas, donde la llamada a la API (aquí simulada por el setTimeout temporizado al azar) se puede realizar en la acción, y después de obtener la respuesta se puede comprometer una mutación, para realizar el cambio al estado .
¿Por qué usar Vuex?
Al crear aplicaciones de gran tamaño, como las aplicaciones de una sola página (SPA), que suelen constar de muchos componentes reutilizables, se pueden volver difíciles de construir y mantener rápidamente. El intercambio de datos y el estado entre estos componentes también puede descomponerse rápidamente y ser difícil de depurar y mantener.
Al utilizar un almacén de datos de aplicaciones centralizado, el estado completo de la aplicación se puede representar en un solo lugar, lo que hace que la aplicación esté más organizada. Mediante el uso de un flujo de datos unidireccional, las mutaciones y el acceso a los datos del componente de alcance solo a los datos requeridos, se vuelve mucho más sencillo razonar sobre el rol del componente y cómo debe afectar el estado de la aplicación.
Los componentes de VueJS son entidades separadas y no pueden compartir datos entre sí fácilmente. Para compartir datos sin vuex necesitamos emit
evento con datos y luego escuchar y capturar ese evento con on
.
componente 1
this.$emit('eventWithDataObject', dataObject)
componente 2
this.$on('eventWithDataObject', function (dataObject) {
console.log(dataObject)
})
Con vuex instalado, simplemente podemos acceder a sus datos desde cualquier componente sin necesidad de escuchar los eventos.
this.$store.state.myData
También podemos cambiar los datos de forma sincronizada con mutadores, utilizar las acciones asíncronos y obtener datos con funciones getter.
Las funciones de Getter podrían funcionar como funciones globales computadas. Podemos acceder a ellos desde componentes:
this.$store.getters.myGetter
Las acciones son métodos globales. Podemos enviarlos desde componentes:
this.$store.dispatch('myAction', myDataObject)
Y las mutaciones son la única forma de cambiar los datos en vuex. Podemos confirmar los cambios:
this.$store.commit('myMutation', myDataObject)
El código de Vuex se vería así
state: {
myData: {
key: 'val'
}
},
getters: {
myGetter: state => {
return state.myData.key.length
}
},
actions: {
myAction ({ commit }, myDataObject) {
setTimeout(() => {
commit('myMutation', myDataObject)
}, 2000)
}
},
mutations: {
myMutation (state, myDataObject) {
state.myData = myDataObject
}
}
¿Cómo instalar Vuex?
La mayor parte del tiempo que utilizará Vuex estará en aplicaciones basadas en componentes más grandes donde probablemente esté utilizando un paquete de paquetes como Webpack o Browserify junto con Vueify si está usando archivos individuales.
En este caso, la forma más fácil de obtener Vuex es desde NPM. Ejecute el siguiente comando para instalar Vuex y guárdelo en las dependencias de su aplicación.
npm install --save vuex
Asegúrese de cargar el enlace de Vuex con su configuración de Vue colocando la siguiente línea después de su declaración de require('vue')
.
Vue.use(require('vuex'))
Vuex también está disponible en CDN; Puede obtener la última versión de cdnjs aquí .
Notificaciones de despido automático
Este ejemplo registrará un módulo vuex dinámicamente para almacenar notificaciones personalizadas que se pueden descartar automáticamente
notificaciones.js
Resolver vuex store y definir algunas constantes.
//Vuex store previously configured on other side
import _store from 'path/to/store';
//Notification default duration in milliseconds
const defaultDuration = 8000;
//Valid mutation names
const NOTIFICATION_ADDED = 'NOTIFICATION_ADDED';
const NOTIFICATION_DISMISSED = 'NOTIFICATION_DISMISSED';
establecer el estado inicial de nuestro módulo
const state = {
Notifications: []
}
establecer nuestros getters módulo
const getters = {
//All notifications, we are returning only the raw notification objects
Notifications: state => state.Notifications.map(n => n.Raw)
}
configura nuestro modulo de acciones
const actions = {
//On actions we receive a context object which exposes the
//same set of methods/properties on the store instance
//{commit} is a shorthand for context.commit, this is an
//ES2015 feature called argument destructuring
Add({ commit }, notification) {
//Get notification duration or use default duration
let duration = notification.duration || defaultDuration
//Create a timeout to dismiss notification
var timeOut = setTimeout(function () {
//On timeout mutate state to dismiss notification
commit(NOTIFICATION_DISMISSED, notification);
}, duration);
//Mutate state to add new notification, we create a new object
//for save original raw notification object and timeout reference
commit(NOTIFICATION_ADDED, {
Raw: notification,
TimeOut: timeOut
})
},
//Here we are using context object directly
Dismiss(context, notification) {
//Just pass payload
context.commit(NOTIFICATION_DISMISSED, notification);
}
}
establecer nuestras mutaciones de módulo
const mutations = {
//On mutations we receive current state and a payload
[NOTIFICATION_ADDED](state, notification) {
state.Notifications.push(notification);
},
//remember, current state and payload
[NOTIFICATION_DISMISSED](state, rawNotification) {
var i = state.Notifications.map(n => n.Raw).indexOf(rawNotification);
if (i == -1) {
return;
}
clearTimeout(state.Notifications[i].TimeOut);
state.Notifications.splice(i, 1);
}
}
Registre nuestro módulo con estado definido, captadores, acciones y mutación.
_store.registerModule('notifications', {
state,
getters,
actions,
mutations
});
Uso
componenteA.vue
Este componente muestra todas las notificaciones como alertas de bootstrap en la esquina superior derecha de la pantalla, también permite descartar manualmente cada notificación.
<template>
<transition-group name="notification-list" tag="div" class="top-right">
<div v-for="alert in alerts" v-bind:key="alert" class="notification alert alert-dismissible" v-bind:class="'alert-'+alert.type">
<button v-on:click="dismiss(alert)" type="button" class="close" aria-label="Close"><span aria-hidden="true">×</span></button>
<div>
<div>
<strong>{{alert.title}}</strong>
</div>
<div>
{{alert.text}}
</div>
</div>
</div>
</transition-group>
</template>
<script>
export default {
name: 'arc-notifications',
computed: {
alerts() {
//Get all notifications from store
return this.$store.getters.Notifications;
}
},
methods: {
//Manually dismiss a notification
dismiss(alert) {
this.$store.dispatch('Dismiss', alert);
}
}
}
</script>
<style lang="scss" scoped>
$margin: 15px;
.top-right {
top: $margin;
right: $margin;
left: auto;
width: 300px;
//height: 600px;
position: absolute;
opacity: 0.95;
z-index: 100;
display: flex;
flex-wrap: wrap;
//background-color: red;
}
.notification {
transition: all 0.8s;
display: flex;
width: 100%;
position: relative;
margin-bottom: 10px;
.close {
position: absolute;
right: 10px;
top: 5px;
}
> div {
position: relative;
display: inline;
}
}
.notification:last-child {
margin-bottom: 0;
}
.notification-list-enter,
.notification-list-leave-active {
opacity: 0;
transform: translateX(-90px);
}
.notification-list-leave-active {
position: absolute;
}
</style>
Fragmento para agregar notificación en cualquier otro componente
//payload could be anything, this example content matches with componentA.vue
this.$store.dispatch('Add', {
title = 'Hello',
text = 'World',
type = 'info',
duration = 15000
});