O Vuex não te restringe em como estruturar seu código. Em vez disso, ele impõe um conjunto de princípios de alto nível:
O estado da aplicação é mantido no armazém, que é um único objeto.
A única maneira de modificar o estado é disparando mutações no armazém.
Mutações devem ser síncronas, e o único efeito colateral que elas devem produzir deve ser a mutação do estado.
Nós podemos expor uma API de mutação do estado mais expressiva ao definir ações. Ações podem encapsular lógica assíncrona como carregamento de dados, e o único efeito colateral que elas devem produzir é disparar mutações.
Componentes usam getters para recuperar estado do armazém, e chamar ações para modificar o estado.
Um fato legal sobre mutações Vuex, ações e getters é que eles são apenas funções. Desde que você siga essas regras, você é quem decide como estruturar sua aplicação. Entretando, é bom termos algumas convenções, assim você se torna familiar a outros projetos que utilizem o Vuex. Abaixo você vê algumas opções dependendo do tamanho do seu aplicativo.
Para um projeto simples, nós podemos simplesmente definir o armazém e as ações em seus respectivos arquivos:
.
├── index.html
├── main.js
├── components
│ ├── App.vue
│ └── ...
└── vuex
├── store.js # exporta todo o armazém (com seu estado inicial e suas mutações)
└── actions.js # exporta todas ações
Para um exemplo real, veja o exemplo do Contador ou o exemplo do TodoMVC.
Como alternativa, você pode separar as mutações para seu próprio arquivo.
Para um app não trivial, nós provavelmente queremos dividir o código relacionado ao Vuex em vários "módulos" (comparáveis a "stores" no Flux, e "reducers" no Redux), cada um lidando com uma parte específica da sua aplicação. Cada módulo estará gerenciando uma parte da árvore do estado da aplicação, exportando o estado inicial para aquela parte da aplicação e todas as mutações que são relativas a ela:
├── index.html
├── main.js
├── api
│ └── ... # abstrações para realizar requisições para a API
├── components
│ ├── App.vue
│ └── ...
└── vuex
├── actions.js # exporta todas ações
├── store.js # onde nós reunimos os módulos e exportamos o armazém
├── mutation-types.js # constantes
└── modules
├── cart.js # estados e mutações para o carrinho
└── products.js # estados e mutações para os produtos
Um módulo se parece com esse exemplo:
// vuex/modules/products.js
import {
RECEIVE_PRODUCTS,
ADD_TO_CART
} from '../mutation-types'
// initial state
const state = {
all: []
}
// mutações
const mutations = {
[RECEIVE_PRODUCTS] (state, products) {
state.all = products
},
[ADD_TO_CART] (state, productId) {
state.all.find(p => p.id === productId).inventory--
}
}
export default {
state,
mutations
}
E no arquivo vuex/store.js
, nós "juntamos" vários módulos para criar a instância Vuex :
// vuex/store.js
import Vue from 'vue'
import Vuex from '../../../src'
// importando partes de módulos
import cart from './modules/cart'
import products from './modules/products'
Vue.use(Vuex)
export default new Vuex.Store({
// combinando submódulos
modules: {
cart,
products
}
})
Aqui, o estado inicial para o módulo cart
será anexado à arvore de estado como store.state.cart
. Além disso, todas as mutações definidades em um submódulo somente recebem a parte do estado da aplicação que está associada a elas. Então mutações definidas no módulo cart
irão receber store.state.cart
como seu primeiro parâmetro.
A raíz de uma subárvore de estado não é substituível dentro do próprio módulo. Por exemplo, o código abaixo não funcionaria:
const mutations = {
SOME_MUTATION (state) {
state = { ... }
}
}
Ao invés disso, sempre armazene o estado atual como uma propriedade da raíz da subárvore:
const mutations = {
SOME_MUTATION (state) {
state.value = { ... }
}
}
Como todos os módulos simplesmente exportam objetos e funções, eles são bem simples de testar e manter, e pode utilizar o hot-reload. Você também tem a liberdade de alterar os padrões utilizados aqui para encontrar a estrutura que se encaixe nas suas preferências.
Note que nós não inserimos ações dentro de módulos, porque uma simples ação pode disparar mutações que afetem vários módulos. Também é uma boa ideia desacoplar as ações do seu estado e dos detalhes de implementação das mutações para uma melhor separação de conceitos. Se os arquivos das ações ficarem muito extensos, você pode criar uma pasta e dividir a implementação em pequenos arquivos individuais..
Para um exemplo real, veja o Exemplo de Carrinho de Compras.