¿Puedo usar Vuex?

SÍ, PERO...

Como vamos a ver a continuación, Vuex sirve para guardar el estado global de la aplicación. Este estado por defecto es volátil, cuando salgamos de la aplicación (ya sea cerrando la ventana, navegando a otro sitio, etc) este estado se borrará. 

Veremos que tenemos una opción para hacer este estado persistente, para poder mantenerlo aunque salgamos de la aplicación, pero esto guarda los datos en el local storage del navegador, los hace accesibles, por lo tanto, podemos usar Vuex, pero bajo ningún concepto podemos perisitir datos de usuario u otros datos sensibles.

Si necesitamos guardar estos datos, debemos hacerlos a través del backend, gestionando sesiones como se explica en esta guía: Gestión de sesiones web.

¿Qué es Vuex?

Vuex es una librería que ofrece Vue para gestionar el estado de la aplicación, centralizar información y funciones, haciéndolas accesibles desde cualquier componente. Se organiza de la siguiente manera:

No todas las aplicaciones necesitarán Vuex, tendrá sentido si se reutiliza el código o si hay componentes distantes que necesiten comunicarse. No tiene sentido, por ejemplo, que hagas un getter si solo lo vas a utilizar una vez y no se comunica con otro componente, para eso mejor una propiedad computed en el mismo componente que lo necesita.

Aquí podemos ver el ciclo de cómo se ejecuta:

Definir un módulo para un servicio

Para definir nuestros módulos, lo haremos bajo la carpeta store/ de nuestro servicio. Por ejemplo:

export default {
  // namespaced para mapear especificando el módulo   ...mapState('nombreModulo', ['estado', 'otroestado'])
  // también para los dispatch -> dispatch("modulo/nombreAction")
  namespaced: true,
  // ESTADO
  state: {
    cuenta: 0,
  },
  // MUTACIONES: sólo estas pueden modificar el estado
  mutations: {
    SUMAR(state, valor) {
      state.cuenta += valor;
    },
    RESETEAR(state) {
      state.cuenta = 0;
    },
  },
  // ACCIONES: se encargan de la lógica de negocio y llaman a las mutaciones
  actions: {
    sumarX({ commit }, valor) {
	  // Lógica
	  // ..
      commit("SUMAR", valor);
    },
    resetear({ commit }) {
	  // Lógica
	  // .. 
      commit("RESETEAR");
    },
  },
  // GETTERS: para propiedades calculadas
  getters: {
    isMillonario: (state) => state.cuenta > 999999,
  },
};

Los módulos tienen que seguir esta notación, la que hemos indicado previamente, para ser leídos correctamente por Vuex.

Añadir módulo al store global de POSE

Para añadir el módulo que hemos creado, lo haremos en el archivo app.store.modules.js, situado en /src. tendremos que importarlo, y añadirlo al final de la lista dentro del export default:

import auth from './auth/store/modules/auth';
import pages from './shared/store/modules/pages';
import language from './shared/store/modules/language';
import notifications from './shared/store/modules/notifications';
import user from './user/store/modules/user';
import services from './campus-virtual/portal-servicios/store/modules/services';
import syllabus from './campus-virtual/historico-guias/store/modules/syllabus';

export default {
  auth,
  pages,
  language,
  notifications,
  user,
  services,
  syllabus,
};

Cómo acceder desde componentes (Options API)

Desde los componentes podemos acceder a la funcionalidad del store de las siguientes formas.

State y Getters

Para el estado y los getters disponemos respectivamente de mapState mapGetters. Estos nos permiten mapearlos a propiedades computed:

import { mapGetters } from "vuex";
import { mapState } from "vuex";
 
export default {
    computed: {
        ...mapState("modulo", ["variableState", "otroState"]),
        ...mapGetters("modulo", ["isTal", "isCual"])
    }    
}

De esta forma tendríamos disponibles estas opciones para llamarlas de la misma forma que al resto de variables o computed variables del componente, con this. en el código JavaScript, y con el nombre directamente en el template.

Si las variables del estado o getters que mapeamos no están en un módulo, sino directamente en elindex.jsde nuestro store, se indicaría directamente el array con sus nombres:

import { mapGetters } from "vuex";
import { mapState } from "vuex";
 
export default {
    computed: {
        ...mapState(["variableState", "otroState"]),
        ...mapGetters(["isTal", "isCual"])
    }    
}

Hay otra forma de referenciar al store y poder acceder tanto al estado como a los getters (también a las acciones como vemos en el siguiente apartado), y sería accediendo a this.$store:

this.$store.state.modulo.variable
this.$store.getters["modulo/getter"]

Mutations

Podemos llamar directamente a las mutaciones si lo que vamos a hacer es modificar una variable del estado directamente. Es decir, no es necesario crear una acción que únicamente haga un commit (que ejecuta una mutación). Por lo tanto, lo haremos así:

// Desde un componente
this.$store.commit("NOMBRE_MUTACION", valor)

// Desde un archivo js, habiendo importado store
store.commit("NOMBRE_MUTACION", valor)

Actions

Para ejecutar acciones desde nuestros componentes, no tendremos que importar nada, simplemente usar dispatch de esta manera:

this.$store.dispatch("modulo/action", datos);

Si la acción no corresponde a ningún módulo, sólo tenemos que llamarla con el nombre de la propia acción. Las acciones son funciones que pueden tener parámetros, y como vemos éstos se le pasan después del nombre de la acción, separados por comas. 

Las acciones sólo toman un parámetro para los datos, por lo que si queremos pasarle varios campos, deberemos utilizar un objeto que los contenga, tanto para pasarlos en el dispatch como en la definición de la acción.

Cómo acceder desde componentes (Composition API)

Se hará como hemos visto en el apartado anterior, pero debemos importar useStore y definirnos una variable store:

<script setup>
import { useStore } from 'vuex';

const store = useStore();
</script>

Después, se accede de igual manera, pero en vez de a través de this.$store, a través de store.


Acceder desde archivos JavaScript

El store ya lo tenemos integrado en los componentes, y ya hemos visto la forma de usarlo. Desde un archivo .js lo utilizaríamos igual, pero en vez de referenciarlo con this.$store, tendríamos que importarlo y acceder directamente a store:

import store from "../store"

store.getters["token/isLogged"];
store.dispatch("modulo/action", parametro);

Persistir estado

Este plugin guarda la parte del estado que indiquemos en el local storage del navegador, es decir, se queda accesible, por lo que debemos asegurarnos de no guardar nada de información sensible.

Para que el estado sea persistente cuando se refresque la página, utilizamos vuex-persistedstate, en concreto el plugin createPersistedState().

Para esto, en primer lugar nos descargamos el paquete de npm:

npm i vuex-persistedstate

Después, como hemos visto antes, podemos especificarlo en la sección plugins de nuestro store (en el index.js, en los módulos no funciona):

import createPersistedState from "vuex-persistedstate";

...
...,
  // Para hacer que el estado persista tras refrescar la página
  plugins: [createPersistedState()]
})

Si lo pasamos sin argumentos, se hará persistente todo el estado que hayamos definido. Podemos utilizar el parámetro paths para definir qué partes del estado persistir:

// Persistir todo. No se puede utilizar en módulos, sólo en la definición del store general
createPersistedState()

// Persistir un módulo con el nombre que tengamos especificado en el bloque de modules, también sólo en el store general
createPersistedState({ paths: ["modulo"] })

// De la misma forma, podemos persistir las variables del state por su nombre
createPersistedState({ paths: ["variableState"] })

// Desde el store general, podemos persistir ciertas partes de los módulos de esta forma
createPersistedState({ paths: ["modulo.variableState"] })