Gerenciamento de Estado com Vuex em Aplicações Vue.js

Barramento de Eventos para Estado

Para comunicação entre componentes não relacionados, pode-se utilizar um barramento de eventos no Vue.js.

Crie uma instância do barramento:

const EventoCentral = new Vue()

Adicione-a ao protótipo Vue para acesso global:

Vue.prototype.$eventoCentral = EventoCentral

Em um componente, dispare um evento:

this.$eventoCentral.$emit('acao-click', 'mensagem de teste')

Em outro componente, escute o evento:

this.$eventoCentral.$on('acao-click', (dados) => {
    this.textoRecebido = dados
})

Vuex: Estado Centralizado

O Vuex fornece um estado reativo centralizado. Para acessá-lo em componentes:

const ComponenteContagem = {
    template: `<div>{{ total }}</div>`,
    computed: {
        total() {
            return this.$store.state.total
        }
    }
}

Use o auxiliar mapState para simplifciar:

import { mapState } from 'vuex'

export default {
    computed: mapState({
        total: estado => estado.total,
        totalAlias: 'total',
        totalComLocal(estado) {
            return estado.total + this.contagemLocal
        }
    })
}

Para mapeamento direto de propriedades:

computed: mapState(['total'])

Combine com computed locais usando o operador spread:

computed: {
    calculoLocal() { /* ... */ },
    ...mapState({
        // mapeamentos adicionais
    })
}

Vuex: Getters com Cache

Getters são semelhantes a propriedades computadas e possuem cache. Exemplo:

const store = new Vuex.Store({
    state: {
        afazeres: [
            { id: 1, descricao: '...', concluido: true },
            { id: 2, descricao: '...', concluido: false }
        ]
    },
    getters: {
        afazeresConcluidos: estado => {
            return estado.afazeres.filter(item => item.concluido)
        }
    }
})

Acesse via propriedade compuatda:

computed: {
    totalConcluidos() {
        return this.$store.getters.totalConcluidos
    }
}

Utilize o auxiliar mapGetters:

import { mapGetters } from 'vuex'

export default {
    computed: {
        ...mapGetters([
            'totalConcluidos',
            'outroGetter'
        ])
    }
}

Vuex: Mutations para Alterações Síncronas

Mutations são a única forma de alterar o estado do Vuex, e devem ser síncronas.

const store = new Vuex.Store({
    state: { contador: 1 },
    mutations: {
        incrementar(estado) {
            estado.contador++
        }
    }
})

Comite uma mutation:

store.commit('incrementar')

Use payloads para dados adicionais:

mutations: {
    incrementar(estado, valor) {
        estado.contador += valor
    }
}
store.commit('incrementar', 5)

Payloads também podem ser objetos:

mutations: {
    incrementar(estado, payload) {
        estado.contador += payload.quantidade
    }
}
store.commit('incrementar', { quantidade: 5 })

Estilo de commit em objeto:

store.commit({
    tipo: 'incrementar',
    quantidade: 5
})

Vuex: Actions para Operações Assíncronas

Actions disparam mutations, mas podem ser assíncronas.

const store = new Vuex.Store({
    state: { contador: 0 },
    mutations: {
        incrementar(estado) {
            estado.contador++
        }
    },
    actions: {
        incrementarAsync(contexto) {
            contexto.commit('incrementar')
        }
    }
})

Desestruture o contexto:

actions: {
    incrementarAsync({ commit }) {
        commit('incrementar')
    }
}

Dispare a action:

store.dispatch('incrementarAsync')

Use payloads de duas maneiras:

store.dispatch('incrementarAsync', { quantidade: 10 })
store.dispatch({ tipo: 'incrementarAsync', quantidade: 10 })

No componente, use mapActions:

import { mapActions } from 'vuex'

export default {
    methods: {
        ...mapActions([
            'incrementar',
            'incrementarPor'
        ]),
        ...mapActions({
            adicionar: 'incrementar'
        })
    }
}

Combine actions com Promises:

actions: {
    acaoA({ commit }) {
        return new Promise((resolver, rejeitar) => {
            setTimeout(() => {
                commit('umaMutation')
                resolver()
            }, 1000)
        })
    }
}
store.dispatch('acaoA').then(() => { /* ... */ })

Ou com async/await:

actions: {
    async acaoA({ commit }) {
        commit('dadosObtidos', await obterDados())
    },
    async acaoB({ dispatch, commit }) {
        await dispatch('acaoA')
        commit('outrosDados', await obterOutrosDados())
    }
}

Vuex: Modules para Organização

Divida o store em módulos:

const moduloA = {
    state: { /* ... */ },
    mutations: { /* ... */ },
    actions: { /* ... */ },
    getters: { /* ... */ }
}

const moduloB = {
    state: { /* ... */ },
    mutations: { /* ... */ },
    actions: { /* ... */ }
}

const store = new Vuex.Store({
    modules: {
        a: moduloA,
        b: moduloB
    }
})
store.state.a // estado do moduloA
store.state.b // estado do moduloB

Vuex: Plugins para Extensibilidade

Plugins podem escutar mutations:

export default store => {
    console.log('Store inicializado')
    store.subscribe((mutacao, estado) => {
        console.log(mutacao) // formato { tipo, payload }
    })
}

Adicione ao store:

const store = new Vuex.Store({
    plugins: [meuPlugin]
})

Para persistência local, use o logger embutido:

import createLogger from 'vuex/dist/logger'

const store = new Vuex.Store({
    plugins: [createLogger()]
})

Tags: Vuex Vue.js Vuex State Vuex Getters Vuex Mutations

Publicado em 8-17 07:13