Métodos esenciales de Vuex para la gestión de estado en Vue.js

Vuex y el concepto de store

Vuex es un patrón de gestión de estado implementado como una biblioteca para aplicaciones Vue.js. Proporciona un almacén centralizado para todos los componentes, asegurando que los cambios en el estado sean predecibles y controlados. El núcleo de una aplicación Vuex es el store, que actúa como un contenedor único para el estado global.

En el desarrollo de software, la compartición de datos entre módulos a menudo conduce a complejidad. Vuex resuelve esto imponiendo reglas claras para las operaciones CRUD en el estado, similar a un sistema de tráfico que regula los flujos. Esta arquitectura promueve estructura y mantenibilidad al centralizar el acceso y las modificaciones.

Los cinco pilares de Vuex

  • state: Almacena los datos base de la aplicación.
  • getters: Proporcionan datos derivados o calculados del estado.
  • mutations: Son los únicos responsables de alterar el estado de forma síncrona.
  • actions: Permiten operaciones asíncronas y coordinan mutations.
  • modules: Facilitan la división del store en subestados independientes.

State: la fuente única de verdad

Vuex emplea un árbol de estado único, lo que significa que un solo objeto contiene todo el estado de la aplicación. Este enfoque simplifica la depuración, ya que se puede inspeccionar el estado completo en cualquier momento. Aunque el estado está centralizado, es compatible con la modularización, permitiendo distribuir lógica en submódulos. No es obligatorio trasladar todo el estado a Vuex; los datos específicos de un componente pueden permanecer locales.

Acceso al estado desde componentes Vue

Para reaccionar a cambios en el estado de Vuex, se utilizan propiedades computadas en los componentes Vue. Debido a la naturaleza reactiva de Vuex, cualquier modificación actualiza automáticamente la interfaz.

const almacen = new Vuex.Store({
    state: {
        total: 0
    }
});

const aplicacion = new Vue({
    store: almacen,
    computed: {
        obtenerTotal() {
            return this.$store.state.total;
        }
    }
});

Uso del helper mapState

Cuando un componente necesita múltiples piezas del estado, mapState evita repetir código generando propiedades computadas automáticamente.

import { mapState } from "vuex";

export default {
    computed: mapState({
        total: estado => estado.total,
        aliasTotal: "total",
        totalLocal: function(estado) {
            return estado.total + this.valorLocal;
        }
    })
};

Para combinar con propiedades computadas locales, se puede usar el operador de propagación.

import { mapState } from "vuex";

export default {
    computed: {
        calculoLocal() { /* ... */ },
        ...mapState({
            total: estado => estado.total
        })
    }
};

Getters: datos derivados con caché

Los getters extraen datos del estado que pueden ser reutilizados, similar a las propiedades computadas en Vue. Se calculan de forma lazy y se cachean para optimizar el rendimiento. Reciben el estado como primer parámetro y otros getters como segundo opcional.

const almacen = new Vuex.Store({
    state: {
        puntos: 0
    },
    getters: {
        puntosDobles(estado) {
            return estado.puntos * 2;
        },
        puntosCuadruple(estado, getters) {
            return getters.puntosDobles * 2;
        }
    }
});

const vm = new Vue({
    store: almacen,
    computed: {
        puntos() { return this.$store.state.puntos; },
        puntosDobles() { return this.$store.getters.puntosDobles; },
        puntosCuadruple() { return this.$store.getters.puntosCuadruple; }
    }
});

Helper mapGetters

mapGetters simplifica la integración de getters en componentes.

import { mapGetters } from "vuex";

export default {
    computed: {
        ...mapGetters(["puntosDobles", "puntosCuadruple"]),
        ...mapGetters({
            doble: "puntosDobles"
        })
    }
};

Mutations: modificaciones síncronas controladas

Las mutations son la única forma de cambiar el estado en Vuex. Cada mutation es una transacción síncrona que recibe el estado y opcionalmente un payload. No se pueden llamar directamente; deben activarse mediante commit.

const almacen = new Vuex.Store({
    state: {
        contador: 1
    },
    mutations: {
        incrementar(estado) {
            estado.contador++;
        },
        sumarN(estado, carga) {
            estado.contador += carga.cantidad;
        }
    }
});

// Para disparar una mutation
this.$store.commit("incrementar");
this.$store.commit("sumarN", { cantidad: 50 });

Dado que el estado es reactivo, las mutations deben seguir las reglas de Vue: inicializar propiedades de antemano y usar Vue.set para añadir nuevas propiedades a objetos. Es crucial que las mutations sean puramente síncronas para facilitar el seguimiento con herramientas de desarrollo.

Helper mapMutations

mapMutations enlaza métodos del componente con commits específicos.

import { mapMutations } from "vuex";

export default {
    methods: {
        ...mapMutations(["incrementar"]),
        ...mapMutations({
            agregar: "sumarN"
        })
    }
};

Actions: lógica asíncrona y coordinación

Las actions son como decoradores que encapsulan mutations, permitiendo operaciones asíncronas. En lugar de mutar el estado directamente, las actions commitan mutations. Reciben un contexto con acceso al estado, getters y a la función commit.

const almacen = new Vuex.Store({
    state: {
        contador: 0
    },
    mutations: {
        incrementar(estado) {
            estado.contador++;
        }
    },
    actions: {
        incrementarAsync({ commit }) {
            setTimeout(() => commit("incrementar"), 2000);
        }
    }
});

this.$store.dispatch("incrementarAsync");
this.$store.dispatch({ type: "incrementarAsync" });

Helper mapActions

mapActions vincula métodos de componente a dispatches de aciones.

import { mapActions } from "vuex";

export default {
    methods: {
        ...mapActions(["incrementarAsync"]),
        ...mapActions({
            asyncIncrement: "incrementarAsync"
        })
    }
};

Composición de actions con Promesas

Las actions pueden retornar Promesas para coordinar flujos complejos. store.dispatch maneja estas Promesas, facilitando la composición de acciones.

const acciones = {
    accionA({ commit }) {
        return new Promise(resolve => {
            setTimeout(() => {
                commit("mutacionA");
                resolve();
            }, 1000);
        });
    },
    accionB({ dispatch, commit }) {
        return dispatch("accionA").then(() => {
            commit("mutacionB");
        });
    }
};

// Uso con async/await
const accionesAsync = {
    async accionC({ commit }) {
        commit("datos", await obtenerDatos());
    },
    async accionD({ dispatch, commit }) {
        await dispatch("accionC");
        commit("otrosDatos", await obtenerOtrosDatos());
    }
};

Modules: modularización del store

Para aplicaciones complejas, Vuex permite dividir el store en módulos. Cada módulo tiene su propio estado, mutations, actions y getters, pudiendo anidarse para estructuras jerárquicas.

const moduloA = {
    state: () => ({ valor: 0 }),
    mutations: {
        actualizarValor(estado, nuevoValor) {
            estado.valor = nuevoValor;
        }
    },
    getters: {
        valorDoble(estado) {
            return estado.valor * 2;
        }
    }
};

const moduloB = {
    state: () => ({ registro: [] }),
    actions: {
        cargarDatos({ commit }) {
            // Lógica asíncrona
        }
    }
};

const store = new Vuex.Store({
    modules: {
        alpha: moduloA,
        beta: moduloB
    }
});

console.log(store.state.alpha.valor);

Espacio de nombres en módulos

Al establecer namespaced: true, los getters, actions y mutations del módulo se encapsulan bajo un prefijo, evitando conflictos. Esto mejora la reutilización y el aislamiento.

const tienda = new Vuex.Store({
    modules: {
        cuenta: {
            namespaced: true,
            state: () => ({ usuario: null }),
            getters: {
                estaAutenticado: estado => !!estado.usuario
            },
            actions: {
                iniciarSesion({ commit }, credenciales) {
                    // Lógica de autenticación
                }
            },
            modules: {
                perfil: {
                    getters: {
                        nombreCompleto(estado, getters, estadoRaiz) {
                            return getters.nombre + " " + estadoRaiz.apellido;
                        }
                    }
                }
            }
        }
    }
});

// Acceso con espacio de nombres
store.dispatch("cuenta/iniciarSesion", datos);
store.getters["cuenta/estaAutenticado"];

Para acceder al estado o getters raíz dentro de un módulo con nombres, se usan los parámetros adicionales en los getters y actions. Si se necesita despachar una acción global, se pasa { root: true } como tercer argumento.

const moduloConNombres = {
    namespaced: true,
    actions: {
        accionLocal({ dispatch }) {
            dispatch("accionRaiz", null, { root: true });
        }
    }
};

Registro dinámico de módulos

Después de crear el store, se pueden registrar módulos de forma dinámica con store.registerModule, lo que permite extensibilidad en plugins o cargas perezosas.

const store = new Vuex.Store({});

store.registerModule("nuevoModulo", {
    state: () => ({ datos: [] }),
    mutations: {
        agregarDato(estado, dato) {
            estado.datos.push(dato);
        }
    }
});

console.log(store.state.nuevoModulo.datos);

Etiquetas: vuex vue.js estado global store mutations

Publicado el 6-13 20:23