Vuex in Vue.js

  • img
    Athul Raj
  • August 1,2019

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:

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:

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:

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:

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:

  1. Application-level state is centralized in the store.

  2. The only way to mutate the state is by committing mutations, which are synchronous transactions.

  3. 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
Subscribe to newsletter
Need more tech news? Tune in to our weekly newsletter to get the latest updates