Vuex es la librería de gestión de estados para Vue.js por excelencia, de hecho Vuex es referenciada desde la la documentación de Vue.js como el gestor de estados oficial. Esto quiere decir que está totalmente soportado por todas las las versiones de Vue.js.

En este punto te estarás preguntando, ¿hay que conocer Vue.js como requisito para empezar a utilizar Vuex? La respuesta es: sí. Utlizar Vuex lleva intrínseco la utilización de Vue.js.

[Captura de pantalla - Logo Vuex]

Si estás comenzando con Vue.js y todavía no te sientes especialmente cómodo trabajando con este framework, te aconsejo que le eches un vistazo a este curso de Aprende Vue JS desaprendiendo jQuery dentro de la Academía online para desarrolladores web.

Vuex pertenece a ese tipo de librerías que cuando te la encuentras de primeras no sabes muy bien para qué te puede servir. Y te preguntarás: cuándo voy a necesitar la gestión de estados en Vue.js entonces? Pues mi respuesta es simple: llegará un momento en que tu aplicación Vue.js crecerá hasta el punto que la comunicación entre componentes se vuelva algo engorrosa; ese es el punto exacto en el que vas a necesitar Vuex, cuando necesites una fuente de verdad de la que todos los componentes de tu aplicación beban (vaya símil el de la fuente y beber, jeje, os prometo que no estaba preparado).

Gestionando estados para mantener un contador

Para entender un poco mejor cómo funciona el mantenimiento de estados de Vue.js, vamos a realizar una pequeña aplicación que nos ayude a controlar un contador que recogerá el número de asistentes a una sala online.

En este caso no nos vamos a centrar en cómo obtenemos estos asistentes, de hecho los cargaremos en un array preestablecido ya que nuestro principal objetivo en este artículo es el de trabajar con estados, incluyendo declaración, lectura y cambio. Estas tres acciones se realizan de forma independiente con distintos métodos que veremos a lo largo del artículo.

Instalando el sistema base

Como ya hemos apuntado anteriormente, nuestra aplicación tiene como base la instalación de Vue.js.

Recordad que podéis encontrar todo el código de esta aplicación en GitHub a través de la dirección: https://github.com/jotajotaramos/assistants-counter-vuex

Para la descarga e instalación de paquetes utilizaremos NodeJS así que es necesario tener instalado este gestor de paquetes antes de ejecutar los comandos npm que veremos a continuación. Podéis instalar el gestor de aplicaciones desde la página oficial del NodeJS.

A continuación instalaremos el cliente de Vue.js. Vue CLI nos permite crear la estructura de ficheros y carpetas base (scaffolding) para poder comenzar a desarrollar nuestra aplicación en Vue.js. Para poder instalar Vue CLI de manera global ejecutaremos el comando:

npm install -g @vue/cli @vue/cli-service-global

La instalación de Vue CLI nos permitirá ejecutar el siguiente comando para poder generar la estructura base de la que hablábamos antes. En este caso llamaremos a nuestra carpeta de aplicación assistants-counter-vuex.

vue create assistans-counter-vuex

Tras la ejecución de este comando la estructura de carpetas/archivos queda de la siguiente forma:

Project
└───node_modules
└───public
└───src
|   .gitigonre
|   babel.config.js
|   package-lock.json
|   package.json
|   README.md

Para poder ver el resultado de la aplicación tendremos que ejecutar un servidor local de desarrollo, para llevar a cabo esta acción sólo deberemos ejecutar el siguiente comando:

npm run serve

Este comando nos permitirá ver la aplicación que genera Vue.js por defecto en el nevegador, a través de la dirección http://localhost:8080/, por defecto.

Por último sólo necesitamos hacer una cosa para poder tener instalado nuestro sistema base, y es instalar Vuex:

npm install vuex

Cómo eliminar las referencias a HelloWorld de la aplicación que Vue.js trae por defecto

Personalmente me gusta crear componentes para cualquier sección de la página (ten siempre en cuenta que crear componentes, es gratis!), así que vamos a crear nuestro primer componente que contenga el título H1 principal de la página.

Dentro de la ruta /src/components creamos un fichero que en este caso llamaremos AssistantsTitle.vue. En este caso no nos vamos a centrar en los estilos, así que el código de este componente quedaría así:

<template>
  <h1>Asistentes a la sesión en directo</h1>
</template>

<script>
export default {

}
</script>

Para tener nuestro código más limpio, vamos a quitar las referencias a la página de bienvenida de Vue.js que viene instalada por defecto. Para ello nos dirijiremos a la ruta src/App.vue (puerta de entrada de la aplicación) y todas las referencias al componente HelloWorld.vue y las sustituiremos por nuestro nuevo componente AssistantsTitle. El archivo App.vuequedaría así (recordad que vamos a quitar todas las referencias a estilos):

<template>
  <div id="app">
    <AssistantsTitle/>
  </div>
</template>

<script>
import AssistantsTitle from './components/AssistantsTitle.vue'

export default {
  name: 'App',
  components: {
    AssistantsTitle
  }
}
</script>

Por último ya podremos eliminar el archivo del componente por defecto HelloWorld situado en /src/components/HelloWorld.vue.

Cómo crear nuestro primer componente

Ahora vamos a enfocarnos en la creación de un componente que tendrá el listado de personas asistentes al directo. En este caso vamos a tener un array fijo de nombres con el listado de asistentes, pero ten en cuenta que puedes traerte este listado de cualquier fuente de datos: una base de datos, una API, etc.

Dentro de la ruta /src/components creamos un fichero que en este caso llamaremos PeopleList.vue. En código de este componente queda así:

<template>

    <div>
        <ul v-for="person in peopleList" :key="person">
            <li>{{person}}</li>
        </ul>
    </div>
    
</template>

<script>
export default {

    data: function (){
        return{
            peopleList:['Ana', 'Juan', 'Pepe', 'Victoria', 'Antonio'],
        }
    }

}
</script>

Ahora vamos a añadir dos botones que añadan y eliminen asistentes en la sala. A casos prácticos estos boton no nos resultarán útiles, pero nos vendrá de perlas a la hora de entender el concepto de estado en Vuex.

El botón añadir, añadirá un asistente con un número random, y el botón eliminar eliminará el primer elemento de la lista de asistentes.

El código del componente queda así:

<template>

    <div>
        <ul v-for="person in peopleList" :key="person">
            <li>{{person}}</li>
        </ul>
        <div>
            <button @click="addAssistant()">Añadir Asistente</button>
            <button @click="deleteAssistant()">Eliminar Asistente</button>
        </div>    
    </div>
    
</template>

<script>
export default {

    data: function (){
        return{
            peopleList:['Ana', 'Juan', 'Pepe', 'Victoria', 'Antonio'],
        }
    },
    methods: {
        addAssistant: function(){
            var randomNumber = Math.floor(Math.random() * 10001);
            this.peopleList.push(`Asistente${randomNumber}`);
        },
        deleteAssistant: function(){
            this.peopleList.splice(0,1);
        }
    }

}
</script>

En este punto nos encontramos con un componente funcional que lista los asistentes a la sala, elimina a uno de ellos (emulando la acción en la que el asistente se va) y anadiendo a uno (emulando la acción en la que entra un nuevo asistente a la sala).

Cómo añadir el store de Vuex a nuestro proyecto

Para poder mantener un contador de asistentes a la sala, vamos a crear un componente que sea capaz de contar el número de asistentes a la sala.

Este es el punto crítico en el que los componentes AssistantsList.vue y el nuevo componente (al que llamaremos AssistantsCount.vue) tienen que transmitirse información sobre el número de asistentes a la sala.

En un enfoque tradicional nos encontrariamos con el viaje de variables entre componentes padres e hijos a través de props. A su vez estos componentes tienen que emitir eventos para que al padre de un cambio en el valor de estas variables.

Cuando tenemos solo un componente padre e hijo estas acciones prometen ser sencillas, pero ¿qué pasa cuando tenemos una gran cantidad de componentes y la información tiene que intercambiarse entre los mismos? En estos casos viene al rescate la "fuente de verdad" Vuex. Un contenedor que albergará toda la información que necesitamos para el intercambio de información entre componentes.

En nuestro caso, el contador de asistentes será la variable que contendrá Vuex en el contenedor principal (llamado store). A estas variables se las llama estados.

La primera acción a tomar es crear un archivo llamado store.jsdentro del directorio /src.

El archivo store.jscontendrá nuestro primer estado al que llamaremos counter. Vamos a ver cómo queda el archivo:

import Vue from "vue"
import Vuex from "vuex"

Vue.use(Vuex)

export default new Vuex.Store({

    state:{

        counter: 5

    }

})

Por defecto, nuestro contador tendrá el valor 5, debido a que en esta aplicación de prueba damos por hecho que hay cinco asistentes en la sala. A efectos prácticos, este contador se inicializará a cero y se irá modificando en función del número de asistente, su incremento y decremento.

A continuación, para que nuestra aplicación utilice el nuevo store creado, necesitamos añadir la referencia a la store en el archivo /src/main.js, de la siguiente forma:

import Vue from 'vue'
import App from './App.vue'
import store from './store'

Vue.config.productionTip = false

new Vue({
  render: h => h(App),
  store
}).$mount('#app')

Cómo leer el nuevo estado creado desde nuestro componente (mapState)

Ahora nos encontramos en el punto en el que tenemos declarado el estado pero queremos visualizarlo en nuestro componente.

Para ello vamos a crear un nuevo componente que mostrará el valor de counter que está almacenado en el store.

Vuex lleva implementados unos denominados Helpers, que nos ayudan a realizar acciones contra los estados. Uno de esos Helpers se llama mapState, y nos ayuda a convertir en variables computadas cada uno de los estados dentro de nuestro componentes.

En nuestro caso vamos a mapear el estado counterpara poder utilizarlo dentro de nuestro nuevo componente.

El archivo /src/components/AssistantsCounter contendrá en siguiente código:

<template>
  <p>El número de asistentes a la sala es: {{counter}}</p>
</template>

<script>
import {mapState} from 'vuex'

export default {
    computed:{
        ...mapState(['counter'])
    }
}
</script>

Cómo modificar el estado desde un componente (mutations)

Por último solo nos queda la posibilidad de modificar nuestro estado contador desde el listado donde se añaden y eliminan asistentes.

Otro de los helpers que implementa Vuex es mutations (mutaciones en español), que nos ayudará a modificar el valor de un estado. La declaración de las mutaciones se realiza en /src/store.js y en su declaración se aconseja utilizar nomeclatura de constantes.

En este caso crearemos una mutación que quite añada uno a nuestro contador para añadir un asistente a la sala y otra que realice la misma acción pero restando el número de asistentes.

Hay que tener en cuenta que a la función que ejecuta la mutación hay que pasarle como parámetro el estado del store para poder utilizar los estados dentro de la misma.

Nuestro store quedaría así:

import Vue from "vue"
import Vuex from "vuex"

Vue.use(Vuex)

export default new Vuex.Store({

    state:{

        counter: 5

    },
    mutations:{
        ADD_PERSON: function(state){

            state.counter++;

        },
        DELETE_PERSON: function(state){

            state.counter--;

        },
    }

})

Ahora sólo tendremos que llamar a estas mutaciuones desde el componentes que realiza la acción de añadir y eliminar personas. En este caso los dos botones contenidos en /src/components/AsistantsList.vue.

La forma de llamar a un commit desde el componente es a través del comando

this.$store.commit([nombreMutación])

Para conocer un poco más el ciclo de funcionamiento de Vuex y cómo las mutaciones toman lugar cuando se realiza un commit, te muestro este gráfico que puede ser muy aclaratorio:

[IMAGEN CICLO FUNCIONAMIENTO VUEX]

Vamos a ver cómo quedaría nuestro componente AssistantsList.vue:

<template>

    <div>
        <ul v-for="person in peopleList" :key="person">
            <li>{{person}}</li>
        </ul>
        <div>
            <button @click="addAssistant()">Añadir Asistente</button>
            <button @click="deleteAssistant()">Eliminar Asistente</button>
        </div>    
    </div>
    
</template>

<script>
export default {

    data: function (){
        return{
            peopleList:['Ana', 'Juan', 'Pepe', 'Victoria', 'Antonio'],
        }
    },
    methods: {
        addAssistant: function(){
            var randomNumber = Math.floor(Math.random() * 10001);
            this.peopleList.push(`Asistente${randomNumber}`);
            this.$store.commit("ADD_PERSON");
        },
        deleteAssistant: function(){
            this.peopleList.splice(0,1);
            this.$store.commit("DELETE_PERSON");
        }
    }

}
</script>

Gracias al cambio de estado aplicado a través de la mutación, cuando añadamos o quitemos un asistente podremos ver reflejado el cambio en el contador que hemos añadido al componente AssistantsCounter.

Conclusiones y cómo continuar tu aprendizaje en Vuex

Gracias a esta pequeña aplicación educativa que hemos realizado, el concepto de contenedor como fuente de verdad de nuestra aplicación se hace visible. Cuando das el siguiente paso en dificultad en tu proyecto Vue.js, Vuex se convierte en una herramienta imprescindible a la hora de gestionar la comunicación entre componentes.

Recuerda que tienes todo el código de la aplicación en GitHub, a través del repositorio https://github.com/jotajotaramos/assistants-counter-vuex

Para ampliar conocimientos te aconsejo también que visites el curso Introducción a Vuex de la academia online de Daniel primo.


Escrito por Juan José Ramos

Soy programador web, artesano del código y estudiante incansable. Aunque mi carrera siempre ha ido ligada al backend , últimamente me estoy dejando llevar por las lindezas del front.
No hay nada que me guste más que indentar y pulsar [ENTER] ;).
comments powered by Disqus