Utilizando Vuex na forma modular

Coisas "fora" do Vuex

Bom.. vou deixar em meu primeiro artigo aqui no blog do Vuejs-Brasil, uma solução que aprendi sobre acessar externamente a store do Vuex e o porquê bati tanto cabeça para algo 'relativamente simples' =P.

Geralmente nossos componentes recebem os estados e ações do Vuex via propriedade vuex no componente:

//MeuComponente.vue
export default {  
    vuex: {
        getters: {
            //aqui ficam as propriedades
        },
        actions: {
            // aqui as ações
        }
    }

Essa é a estrutura básica de como se obter as propriedades armazenadas na store e suas mutations (actions).

Mas… e quando quisermos “abstrair” uma funcionalidade global?

Por exemplo:

No meu caso eu gostaria muito de ter um serviço de autenticação independente de componente, ou seja, que ele ficasse contido dentro de um arquivo e que consultasse a store diretamente sem a necessidade de encapsular ele dentro de um componente como no código acima.

Se tu procurares aí na internet a fora verás muitos exemplos como do código acima, e na própria documentação do Vuex está assim. Outro trecho de código comum que tu achas por aí para inicializar a store é algo deste tipo:

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

Vue.use(Vuex)

export default new Vuex.Store({...})  

E lá na instância do Vue ou do componente App (no caso de usar o vue-cli) o seguinte código:

// main.js
import Vue from 'vue'  
import store from 'store'

new Vue({  
    store, //<- injeta a store dentro da instância do Vue
    ...
})

Procurei, procurei até que resolvi arriscar.. e confesso não ser um programador javascript avançado, ou até mesmo intermediário, mas a coisa é que muitos anos no C# (C-Sharp) ainda pesam sobre minhas decisões no JS!

Bom… eu já tinha a store no meu compomente App.vue e ela ficava disponível para toda aplicação, OK, mas lembra o meu serviço de autenticação (authService) ?

Então foi aí que resolvi arriscar e importar o store pra dentro de um arquivo chamado index.js dentro de uma pasta com o nome de authService e voilà!!!! Agora bastava eu importar authService que eu tinha acesso ao conteúdo da store. Mas espera! Não foi por causa da pasta e tão menos os nomes, fiz isso justamente para organizar o código (pretendo escrever sobre isso em outra oportunidade).

Agora posso ter o serviço da seguinte forma:

// main.js
import Vue from 'vue'  
import store from 'store'

new Vue({  
    store,
    ...
})
//index.js em /vuex/auth

const SET_TOKEN = 'SET_TOKEN'  
const state = {  
        token: ''
    }
const mutations = {  
      [SET_TOKEN] (state, token) {
         state.token = token
    }

export default { state, mutations }  
// store.js
import Vue from 'vue'  
import Vuex from 'vuex'

//importo o state e mutations
import auth from './vuex/auth'

Vue.use(Vuex)

export default new Vuex.Store({  
    modules: {
        auth
    }
})

E por fim... o authService:

// index.js em /services/authService
import store from '../../vuex/store'

//mudando o valor do token diretamente na store!
const setToken = function(token) {  
    store.dispatch('SET_TOKEN', token)
}
//recuperando o valor de token da store!
const getToken = function() {  
    return store.state.auth.token
}

Quando eu precisar usar o serviço de autenticação para gravar o token a partir de algum componente eu faço da seguinte forma:

//MeuComponente.vue
<script>  
    import authService from '/services/authService'
    export default {
        methods: {
            UmMetodoQualquer () {
                // ... faz algo e recebo o token //
                let token = 'a23dd343...sde343231'
                // gravo o token
                authService.setToken(token)
                // ... 
                // o mesmo pode ser feito para recuperar o token
                token = authService.getToken()
            }
        }
</script>  

O resultado ficou ótimo!

Moral da história!

Eu tava era preocupado com o new Vuex.Store gerar uma “nova instância” e perder os dados que já haviam nela!!!! hahahahahahahaha

Obrigado por ler! ;)


Vamos entender o que aconteceu.

Oi, eu sou Vinicius.
Em resumo javascript é uma linguagem de script interpretada em runtime, e entre outras características o js possui escopo léxico, que nos permite criar clousures.

Em javascript tudo (tirando undefined) são objetos, javascript (sozinho) não possui suporte a módulos ou imports, isso significa que você não pode "incluir" um arquivo js durante a execução do seu script.
Porém, há especificações para criar módulos javascript. Os mais conhecidos são AMD e CommonJS. Eles são parecidos e podem funcionar em conjunto.
Há ferramentas como requirejs que ajudam a usar módulos, e ferramentas como o browserify ou webpack que deixam isso mais simples ainda.

Assumimos que um arquivo javascript é 'módulo javascript', e um módulo expõe valores. Um módulo é basicamente um objeto servindo como namespace para valores, que podem ser outros objetos.

Os módulos js fazem uso da técnica de clousure do javascript para criar valores privados e públicos, além disso os módulos são (por muitas vezes) IIFE (immediately-invoked function expression) sendo o "retorno" dessas funções um "singleton".

E é nesse ponto que entendemos o que aconteceu.

export default new Vuex.Store({...})  

Estamos expondo uma instância do Vuex.Store. Qualquer um que importar este arquivo terá acesso a essa instância, e ela sempre será a mesma (singleton). Em javascript os objetos são passados como referência, então todos os locais que usam esse módulo estão usando a mesma referência.

Esse comportamento é extremamente útil e se você não gosta dele há maneira simples de contornar.

Essa é uma explicação rasa. Inicialmente javascript pode parecer algo muito complexo, porém estudando os conceitos por trás dos comportamentos tudo se torna muito simples.