Skip to content

Vuex - Gerenciamento Centralizado de Estado no Vue.js

Cuidado

Este artigo é voltado para o Vue 2. O Vue 3 agora está disponível e é a maneira recomendada de desenvolver aplicativos com Vue.

Soluções comuns para enviar e receber dados entre páginas incluem passar dados via URL, cookies e LocalStorage. Embora funcionais, esses métodos não fornecem um padrão para manipulação de dados e não são fontes de dados reativas que se harmonizam com a reatividade do Vue.js.

Evan You, criador do Vue.js, desenvolveu uma ferramenta chamada Vuex projetada especificamente para o Vue que permite o gerenciamento centralizado de estado em aplicações web construídas com este framework. Embora não seja a única ferramenta de gerenciamento de dados, ela se integra melhor com o ecossistema Vue.

Vuex é uma biblioteca de padrões de gerenciamento de estado para aplicativos Vue.js. Ele fornece um store centralizado para todos os componentes da aplicação, com regras garantindo que o estado só possa ser mutado de uma forma previsível.

O uso do Vuex é especialmente recomendado para aplicativos médios a grandes que exigem dados centralizados e reativos acessíveis em diferentes componentes.

Instalação

Via CDN, colocando-o após o vue:

html
<script src="/caminho/para/vue.js"></script>
<script src="/caminho/para/vuex.js"></script>

Usando NPM:

javascript
npm install vuex --save

Usando Yarn:

javascript
yarn add vuex

Ao usar um sistema de módulos (como webpack no vue-cli), você precisa instalar explicitamente o Vuex via Vue.use():

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

Vue.use(Vuex);

Estrutura Básica do Vuex

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

state

O state é o objeto que contém todos os dados que podemos acessar a partir dos diferentes componentes. Seu conteúdo só deve ser modificado através de mutations para que sua modificação seja transparente e auditável.

javascript
state: {
  count: 0
}

mutations

A única maneira de mudar o estado em um store Vuex é fazendo commit de uma mutation. Estas funções realizam as modificações e recebem o state como primeiro parâmetro.

javascript
mutations: {
  increment (state) {
    state.count++
  }
}

Mutações não podem ser chamadas diretamente, então elas são executadas com store.commit:

javascript
store.commit('increment');

Mutações também podem receber dados como um segundo parâmetro, que pode ser um número, string, array, etc.

javascript
mutations: {
  increment (state, amount) {
    state.count += amount  
  }
}
javascript
store.commit('increment', 10);

Um ponto importante é que mutações são síncronas, o que significa que quaisquer mudanças de estado devem ser feitas de uma só vez e não através de transações assíncronas como consultas de banco de dados ou API. Para mudanças de estado assíncronas, actions são usadas.

actions

actions são semelhantes às mutações, exceto por duas diferenças:

  • Elas são despachadas com store.dispatch
  • Em vez de mutar o estado, actions fazem commit de mutações
  • actions podem conter código assíncrono
  • Elas recebem um objeto context como primeiro argumento que nos dá acesso ao state, mutations, actions e getters
  • actions podem retornar uma promise uma vez resolvida

Suponha que quiséssemos incrementar o contador após consultar alguma API, poderíamos fazer isso assim:

javascript
actions: {
  asyncIncrement (context) {
    return new Promise((resolve, reject) => {
      fetch('someApiX').then(() => {
        context.commit('increment')
        resolve()
      }) 
    })
  }
}

ou usando async/await:

javascript
actions: {
  async asyncIncrement (context) {
    await fetch('someApiX')
    context.commit('increment') 
  }
}

e usá-lo assim:

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

Saiba mais sobre Promises em JavaScript.

getters

getters são usados para recuperar informações processadas do state. Eles recebem o state como primeiro argumento.

Supondo que tivéssemos uma lista de tarefas no state, poderíamos criar um getter que retorna apenas as tarefas concluídas:

javascript
state: {
  tasks: [
    { id: 1, text: 'lorem ipsum', done: true },
    { id: 2, text: 'lorem ipsum', done: true },
    { id: 3, text: 'lorem ipsum', done: false },  
    { id: 4, text: 'lorem ipsum', done: false },
    { id: 5, text: 'lorem ipsum', done: true },
  ]
},
getters: {
  doneTasks (state) {
    return state.tasks.filter(task => task.done)
  }  
}

e usá-lo assim:

javascript
store.getters.doneTasks;
/*
[
  { id: 1, text: 'lorem ipsum', done: true }, 
  { id: 2, text: 'lorem ipsum', done: true },
  { id: 5, text: 'lorem ipsum', done: true },  
]
*/

getters também podem receber dados retornando uma função. Então poderíamos recuperar uma tarefa específica por id através de um getter assim:

javascript
getters: {
  taskById: (state) => (id) => {
    return state.tasks.find(task => task.id === id)
  }
}

E usá-lo assim:

javascript
store.getters.taskById(2); // { id: 2, text: 'lorem ipsum', done: true }

Dessa forma, podemos gerenciar dados acessíveis em nossos componentes, que são mutáveis por meio de mutações e ações e que podemos consultar de forma processada por meio de getters. Dando à nossa aplicação um padrão padrão de uso de dados que nos permite escalar mais rapidamente.