Vuex

Compreendendo o problema que ele resolve

Quando você está construindo uma aplicação baseada no browser não é incomum perder o controle sobre o fluxo da informação que é gerenciada pela app.

Nestes tipos de aplicação os dados não são apenas o que o usuário envia ao servidor mas também o que controla a exibição de controles de interface. Então - por exemplo - você modifica um valor num componente e outro componente que deveria estar visível agora está escondido. Isso se chama efeito colateral.

Conforme sua aplicação cresce torna-se um pesadelo lidar com todos os efeitos colaterais que aparecem. A forma mais comum de compartilhar dados através da árvore de componentes é utilizando eventos, acima e abaixo na árvore. Isso é OK quando você captura um evento imediatamente após dispara-lo mas quando o evento é capturado mais acima ou mais abaixo na árvore, você rapidamente se pegará pensando "- mas de onde veio este evento?".

Single Source of Truth

More on Wikipedia

Single Source of Truth (ou Fonte Única da Verdade) é estruturar seus dados de maneira que este não são duplicados em nenhum lugar da app. Pense nela como a espinha dorsal da aplicação. Todos os componentes pegarão os dados dela e salvarão de volta, tornando muito mais fácil saber de onde a alteração veio.

Há algumas vantagens ao se adotar esta abordagem:

  1. Você tem um local centralizado para adicionar/modificar seus dados;
  2. Está disponível para todos os componentes;
  3. Nenhum componente modifica a informação diretamente, garantindo a consistência dos dados;
  4. Ferramentas adicionais fazem do desenvolvimento uma experiência melhor.

Vuex

Documentação oficial aqui

É a implementação feita pela equipe Vue.js para o Flux, que é a implementação feita pelo Facebook da Single Source of Truth. A integração com o sistema reativo do Vue é transparente e requer apenas algumas configurações bem simples, ficando imediatamente pronto para uso.

O primeiro passo é instalar utilizando npm install vuex --save-dev. Logo em seguida vá ao arquivo no qual você criou sua instância do Vue Object, instrua o Vue para usar o Vuex e adicione a Store ao objeto:

import Vue from 'vue'  
import Vuex from 'vuex'

Vue.use(Vuex)

new Vue({  
  el: 'body',
  store: new Vuex.Store({})
})

Dentro do método Vuex.Store() você terá que passar um objeto com duas propriedades, como segue:

  1. O State, que é um objeto comum Javascript, contendo os dados que você quer compartilhar através da aplicação;
  2. Os métodos que alteram o State. Eles são chamados de Mutation Methods.

Como example veja o trecho de código:

...
new Vue({  
  el: 'body',
  store: new Vuex.Store({
    state: {
      user: {
        name: '',
        email: ''
      }
    },
    mutations: {
      SET_USER (store, obj) {
        store.user = obj.user
      }
    }
  })
})

Como você deve ter imaginado tanto a quantidade de propriedades em State quanto a de métodos em Mutations crescerá bastante. Usemos então o module bundler (Webpack, por exemplo) para fazer o setup do Vuex em algum outro local em nosso File System e então importa-lo:

criar o arquivo: /src/vuex/store.js

import Vue from 'vue'  
import Vuex from 'Vuex'

Vue.use(Vuex)

export default new Vuex.Store({  
    state: {
      user: {
        name: '',
        email: ''
      }
    },
    mutations: {
      SET_USER (store, obj) {
        store.user = obj.user
      }
    }
  })

e no seu /src/main.js:

import Vue from 'vue'  
import store from './vuex/store'

Vue.use(Vuex)

new Vue({  
  el: 'body',
  store
})

Lendo e gravando dados na Store

Agora que temos nossa store totalmente configurada e anexada ao objeto Vue é hora de começar a utiliza-la.

A beleza da coisa é que a Store estará automaticamente disponível a todos os componentes. Da mesma forma que você está acostumado a usar propriedades tais como methods: {}, data: {} and computed: {} agora você também terá vuex: {}.

<script>  
  export default {
    props: ['some-property-here'],

    vuex: {
      getters: {},
      actions: {}
    },

    data () { // << some local state
      return {
        whatever: ''
      }
    }
  }
</script>

Se se quer acessar a propriedade user da Vuex Store basta simplesmente criar um getter...

...
vuex: {  
  getters: {
    user: store => store.user
  },
  actions: {}
},
...

... e então utilizar como uma propriedade interna do componente: this.user.

Agora você precisa modificar os dados do usuário. Antes de seguirmos adiante uma nota mental: você NÃO PODE modificar o State diretamente. Se me perguntar se é possível eu diria "sim é" porém altamente desencorajado. Isso por que se quiser saber o que acontece quando qualquer propriedade for modificada, basta ir no único local onde isso acontece, ao invés de abrir todos os seus componentes para só então descobrir de onde a alteração partiu.

Então, como modificar os dados?

Utilizando actions. Elas são métodos como os que você costuma criar dentro de methods: {} em seus componentes mas você os declara num local especial: dentro de vuex: { actions: {} }. Ao fazer isso você se assegura de que o primeiro parâmetro recebido pelo método será uma instância da Vuex Store.

Estamos interessados num método do objeto Store chamado dispatch(). Usaremos este método para invocar a Mutation, método responsável por setar os dados na Vuex Store.

...
vuex: {  
  getters: {
    user: store => store.user
  },
  actions: {
    setUser ({dispatch}, obj) {
      dispatch('SET_USER', obj)
    }
  }
},

methods: {  
  ordinaryButtonClickHandler () {
    let user = {
      user: {
        username: 'New username',
        email: [email protected]'
      }
    }
    this.setUser(user) // << a action é executada por um clique num botão, por ex.
  }
}
...

Preste atenção a esta parte pois é a mais confusa: dispatch('SET_USER', obj). Primeiro recebemos este método dispatch() ao utilizar Destructuring Assignment, um novo e muito útil recurso do ES2015. Pense no método dispatch() como um event dispatcher com um único objetivo: invocar uma mutation. Você não precisa captura-lo manualmente como faria com um evento comum. Graças ao Vue ele vai diretamente para o objeto de mutations na sua configuração do Vuex e então o método adequado é invocado.

O nome do método da mutation é o primeiro parâmetro e o objeto que a mutation receberá é o segundo. Quando chegar à mutation a Store é finalmente modificada e todos os componentes observando a Store serão automaticamente atualizados.

Ferramentas de Suporte ao Desenvolvimento

Existe uma ferramenta muito poderosa que dá suporte ao desenvolvimento com Vue.js. Ela se chama Vue Devtools e é uma extensão para o Google Chrome. Permite que você inspecione sua árvore de componentes, interagindo com eles no Console. Lhe permite também interagir com Vuex Store, tirando vantagem do recursos chamado Time Travel Debugging: mostra todas as mutations que foram executadas permitindo navegar entre elas e vendo alterações na aplicação em tempo real.

Conclusão

A documentação oficial diz que Vuex não é adequado para todos os tipo de projeto mas eu acho um recurso tão útil e simples de usar que atualmente incluo em todos os meus projetos. Simplesmente me parece algo natural de ser feito.

Agora que está claro que Vuex é um local centralizado para armazenar seus dados, o que torna muito mais fácil gerenciar o estado da sua aplicação, tenha em mente que você também pode (e deve) manter o estado local dos seus componentes. A decisão sobre o que pertence à Store central ou ao state interno do componente é você quem deve tomar. Se você precisa daquele pedaço de informação em algum outro local da aplicação então você deve adicionar à Vuex Store.