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.