Skip to content

Vuex - Manejo centralizado de datos en Vue.js

Entre las soluciones comúnmente usadas para enviar y recibir información entre páginas se encuentra el envío de datos a través del URL, las cookies y el LocalStorage. Todas éstas formas si bien son funcionales no proveen de un estándar para la manipulación de los datos y no son fuentes de datos reactivas que hagan armonía con la reactividad de Vue.js.

Evan You, creador de Vue.js, desarrolló una herramienta llamada Vuex especialmente diseñada para Vue que permite el manejo centralizado de datos en aplicación web desarrolladas con este framework. Si bien no es la única herramienta para el manejo de datos, es la que mejor se integra con el ecosistema de Vue.

Vuex es una librería de manejo de patrones de estado para aplicaciones Vue.js. Provee un almacenamiento centralizado para todos los componentes en la aplicación, con reglas que aseguran que los datos sólo puedan ser modificados de forma predecible.

Usar Vuex es especialmente recomendado para aplicaciones medianas y grandes donde se requiere información centralizada y reactiva accesible a través de distintos componentes.

Instalación

A través de un CDN, colocándolo después de vue

html
<script src="/path/to/vue.js"></script>
<script src="/path/to/vuex.js"></script>

Utilizando NPM

javascript
npm install vuex --save

Utilizando Yarn

javascript
yarn add vuex

Al usar un sistema de módulos (como webpack en el vue-cli), es necesario instalar Vuex a través de Vue.use()

javascript
import Vue from 'vue';
import Vuex from 'vuex';

Vue.use(Vuex);

Estructura básica de Vuex

javascript
const store = new Vuex.Store({
  state: {},
  mutations: {},
  actions: {},
  getters: {},
});

state

El state es el objeto que contiene todos los datos que podemos acceder desde los distintos componentes. Su contenido sólo debe ser modificado a través de las mutations de tal forma que la modificación del mismo sea transparente y auditable.

javascript
state: {
  contador: 0;
}

mutations

La única forma de modificar el state de un almacenamiento Vuex es a través de una mutation. En estas funciones es que se realizan las modificaciones y las mismas reciben el statecomo primer parámetro.

javascript
mutations: {
  incrementar (state) {
    state.contador++
  }

Las mutaciones no se pueden llamar directamente, por lo que son ejecutadas con store.commit

javascript
store.commit('incrementar');

Las mutaciones también pueden recibir datos como segundo parámetro, esto puede ser un número, una cadena, un arreglo, etc...

javascript
mutations: {
  incrementar (state, cantidad) {
    state.contador += cantidad
  }
javascript
store.commit('incrementar', 10);

Un punto importante es saber que las mutaciones son síncronas, es decir cualquier modificación al statedebe realizar de una vez y no a través de transacciones asíncronas como consultas a bases de datos o APIs. Para modificaciones asíncronas al state se utilizan los actions.

actions

Los actions son similares a las mutaciones excepto por dos diferencias:

  • Se ejecutan con store.dispatch
  • En vez de modificar el state, los actions realizan mutaciones.
  • Los actions pueden contener código asíncrono.
  • Reciben como primer parámetro una variable context que nos da acceso al state y a las mutations, actions y getters.
  • Los actions pueden retornar una promesa una vez son resueltos.

Suponiendo que quisiéramos incrementar el contador después de una consulta a algun API podríamos hacerlo de la siguiente forma

javascript
actions: {
  incrementoAsincrono (context) {
    return new Promise((resolve, reject) => {
      fetch('algunApiX').then(() => {
        context.commit('incrementar')
        resolve()
      })
    })
  }
}

o utilizando async/await

javascript
actions: {
  async incrementoAsincrono (context) {
    await fetch('algunApiX')
    context.commit('incrementar')
  }
}

y utilizarlo de la siguiente forma

javascript
store
  .dispatch('incrementoAsincrono')
  .then(() => console.log('¡Contador incrementado!'));

Conoce más sobre Promesas de Javascript.

getters

Los getters son utilizados para traer información del state de forma procesada. Estos reciben el state como primer argumento.

Suponiendo que en el statetuviéramos un listado de tareas, podríamos crear un getter que retorne solamente las tareas completadas.

javascript
state: {
  tareas: [
  { id: 1, texto: 'lorem ipsum', realizada: true },
  { id: 2, texto: 'lorem ipsum', realizada: true },
  { id: 3, texto: 'lorem ipsum', realizada: false },
  { id: 4, texto: 'lorem ipsum', realizada: false },
  { id: 5, texto: 'lorem ipsum', realizada: true },
  ],
},
getters: {
  tareasRealizadas (state) {
    return state.tareas.filter(tarea => tarea.realizada)
  }
}

y usarlo de esta forma

javascript
store.getters.tareasRealizadas;
/*
[
  { id: 1, texto: 'lorem ipsum', realizada: true },
  { id: 2, texto: 'lorem ipsum', realizada: true },
  { id: 5, texto: 'lorem ipsum', realizada: true },
]
*/

Los getters también pueden recibir datos retornando una función. Es decir que podríamos traer una tarea específica por id a través de un getter de la siguiente forma

javascript
getters: {
  tareasRealizadasPorId (state) => (id) => {
    return state.tareas.filter(tarea => tarea.id === id)
  }
}

Y usarlo así

javascript
store.getters.tareasRealizadasPorId(2); // [{ id: 2, texto: 'lorem ipsum', realizada: true }]

De esta forma podemos manejar información accesible en nuestros componentes la cual es manipulable a través de los mutations y los actions y la cual podemos consultar de forma procesadas a través de los getters. Dándole a nuestra aplicación un patrón de uso de datos estándar que nos permite escalar más rápidamente.