Vuex is a state management pattern + library for Vue.js applications. It serves as a centralized store for all the components in an application, with rules ensuring that the state can only be mutated in a predictable fashion.
What is a "State Management Pattern"?
Let's start with a simple Vue counter app:
new Vue({
// state
data () {
return {
count: 0
}
},
// view
template: `
<div>{{ count }}</div>
`,
// actions
methods: {
increment () {
this.count++
}
}
})
It is a self-contained app with the following parts:
- The state, the source of truth that drives our app;
- The view, a declarative mapping of the state;
- The actions, the possible ways the state could change in reaction to user inputs from the view.
This is an simple representation of the concept of "one-way data flow":

However, the simplicity quickly breaks down when we have multiple components that share a common state:
- Multiple views may depend on the same piece of state.
- Actions from different views may need to mutate the same piece of state.
So to overcome this problem an approach is choosen
why don't we extract the shared state out of the components, and manage it in a global singleton? With this, our component tree becomes a big "view", and any component can access the state or trigger actions, no matter where they are in the tree!
This is the basic idea behind Vuex.
Getting Started
At the center of every Vuex application is the store. A "store" is basically a container that holds your application state. There are two things that make a Vuex store different from a plain global object:
- Vuex stores are reactive. When Vue components retrieve state from it, they will reactively and efficiently update if the store's state changes.
- You cannot directly mutate the store's state. The only way to change a store's state is by explicitly committing mutations.
- This ensures every state change leaves a track-able record, and enables tooling that helps us better understand our applications.
After installing Vuex, let's create a store. It is pretty straightforward - just provide an initial state object, and some mutations:
// Make sure to call Vue.use(Vuex) first if using a module system
const store = new Vuex.Store({
state: {
count: 0
},
mutations: {
increment (state) {
state.count++
}
}
})
Now, you can access the state object as store.state
, and trigger a state change with the store.commit
method:
store.commit('increment')
console.log(store.state.count) // -> 1
Lets dig into some core concepts of Vuex
1.State
Vuex uses a single state tree - that is, this single object contains all your application level state and serves as the "single source of truth". This also means usually you will have only one store for each application.
Getting Vuex State into Vue Components
Since Vuex stores are reactive, the simplest way to "retrieve" state from it is simply returning some store state from within a computed property:
// let's create a Counter component
const Counter = {
template: `<div>{{ count }}</div>`,
computed: {
count () {
return store.state.count
}
}
}
Whenever store.state.count
changes, it will cause the computed property to re-evaluate, and trigger associated DOM updates.
2.Mutations
The only way to actually change state in a Vuex store is by committing a mutation. Vuex mutations are very similar to events: each mutation has a string type and a handler. The handler function is where we perform actual state modifications, and it will receive the state as the first argument:
const store = new Vuex.Store({
state: {
count: 1
},
mutations: {
increment (state) {
// mutate state
state.count++
}
}
})
You cannot directly call a mutation handler. Think of it more like event registration: "When a mutation with type increment
is triggered, call this handler." To invoke a mutation handler, you need to call store.commit
with its type:
store.commit('increment')
3. Actions
Actions are similar to mutations, the differences being that:
- Instead of mutating the state, actions commit mutations.
- Actions can contain arbitrary asynchronous operations.
Let's register a simple action:
const store = new Vuex.Store({
state: {
count: 0
},
mutations: {
increment (state) {
state.count++
}
},
actions: {
increment (context) {
context.commit('increment')
}
}
})
Dispatching Actions
Actions are triggered with the store.dispatch
method:
store.dispatch('increment')
Application Structure
Vuex doesn't really restrict how you structure your code. Rather, it enforces a set of high-level principles:
- Application-level state is centralized in the store.
- The only way to mutate the state is by committing mutations, which are synchronous transactions.
- Asynchronous logic should be encapsulated in, and can be composed with actions.
As long as you follow these rules, it's up to you how to structure your project. If your store file gets too big, simply start splitting the actions, mutations and getters into separate files.
For any non-trivial app, we will likely need to leverage modules. Here's an example project structure:
├── index.html
├── main.js
├── api
│ └── ... # abstractions for making API requests
├── components
│ ├── App.vue
│ └── ...
└── store
├── index.js # where we assemble modules and export the store
├── actions.js # root actions
├── mutations.js # root mutations
└── modules
├── cart.js # cart module
└── products.js # products module