Skip to the content.

Vuex Store with Vue 2 - CDN


1. What is Vuex?

Vuex is a state management library for Vue.js applications. It helps manage shared data (state) across multiple components in a predictable way.

Why Vuex?

Without Vuex (Using Props and Emit)

In Vue 2, components communicate using:

Example:

Parent:

<child-component :count="count" @increment="count++"></child-component>

Child:

props: ['count'],
methods: {
  updateCount() {
    this.$emit('increment')
  }
}

Problem:

Example issue (Props Drilling): HomePage → ProductList → ProductCard → AddToCartButton (e-commerce cart example)

Even if only AddToCartButton needs data, all intermediate components must pass it.

This makes:


With Vuex

Instead of passing data manually:

Example:

this.$store.state.count
this.$store.commit('increment')

Benefits:


Key Idea (Why this matters)

Let us understand with a clear situation.

Scenario:

Without Vuex (using props & emit):

Step 1: HomePage passes data to ProductList

<product-list :cartCount="cartCount" @add="cartCount++"></product-list>

Step 2: ProductList must pass it again (even if it does not use it)

<product-card :cartCount="cartCount" @add="$emit('add')"></product-card>

Step 3: ProductCard must pass it again

<add-to-cart-button :cartCount="cartCount" @add="$emit('add')"></add-to-cart-button>

Step 4: AddToCartButton finally uses it

Problem here:

This is called props drilling


With Vuex:

Any component can directly access and update state:

AddToCartButton:

this.$store.state.count
this.$store.commit('increment')

No need for:


Final Understanding:

Props/Emit:

Vuex:

This is why Vuex is introduced in larger applications.


2. Core Concepts of Vuex

(Reference: https://v3.vuex.vuejs.org/)

2.1 State

(Reference: https://v3.vuex.vuejs.org/guide/state.html)

Example:

state: {
  count: 0
}

2.2 Getters

(Reference: https://v3.vuex.vuejs.org/guide/getters.html)

Example:

getters: {
  doubleCount: (state) => state.count * 2
}

2.3 Mutations

(Reference: https://v3.vuex.vuejs.org/guide/mutations.html)

Example:

mutations: {
  increment(state) {
    state.count++
  }
}

2.4 Actions

(Reference: https://v3.vuex.vuejs.org/guide/actions.html)

Example:

actions: {
  incrementAsync({ commit }) {
    setTimeout(() => {
      commit('increment')
    }, 1000)
  }
}

2.5 What is context in Actions?

(References: https://v3.vuex.vuejs.org/guide/actions.html, https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Destructuring_assignment)

In Vuex, every action receives a first argument called context.

This context object is provided by Vuex and contains useful store methods/properties like:

So when we write:

incrementAsync({ commit }) {
  setTimeout(() => {
    commit('increment')
  }, 1000)
}

{ commit } is JavaScript object destructuring. It means: take only the commit property from the full context object.

Equivalent long form:

incrementAsync(context) {
  setTimeout(() => {
    context.commit('increment')
  }, 1000)
}

Both are correct. The destructuring form is shorter and commonly used.


3. Vuex Flow

(Reference: https://v3.vuex.vuejs.org/guide/actions.html#dispatching-actions)

Component -> Dispatch Action -> Commit Mutation -> Update State -> UI updates


4. Using Vuex with CDN (Vue 2)

<script src="https://cdn.jsdelivr.net/npm/vue@2"></script>
<script src="https://cdn.jsdelivr.net/npm/vuex@3"></script>

5. Project Structure

index.html
app.js
store.js

6. Creating the Store (store.js)

const store = new Vuex.Store({
  state: {
    count: 0
  },

  getters: {
    doubleCount(state) {
      return state.count * 2
    }
  },

  mutations: {
    increment(state) {
      state.count++
    },
    decrement(state) {
      state.count--
    }
  },

  actions: {
    incrementAsync({ commit }) {
      setTimeout(() => {
        commit('increment')
      }, 1000)
    }
  }
})

7. Using Store in Vue Instance (app.js)

new Vue({
  el: '#app',
  store,

  // data is still used, but only for local component state
  data() {
    return {
      message: 'Local UI state'
    }
  },

  computed: {
    // shared/global state comes from Vuex
    count() {
      return this.$store.state.count
    },
    doubleCount() {
      return this.$store.getters.doubleCount
    }
  },

  methods: {
    increment() {
      this.$store.commit('increment')
    },
    decrement() {
      this.$store.commit('decrement')
    },
    incrementAsync() {
      this.$store.dispatch('incrementAsync')
    }
  }
})

Important Understanding

Example:

Why not use only data?

So rule of thumb:


Computed vs Data

Wrong:

data() {
  return { count: this.$store.state.count }
}

(Not reactive)

Correct:

computed: {
  count() { return this.$store.state.count }
}

Commit vs Dispatch

this.$store.commit('increment')
this.$store.dispatch('incrementAsync')

Do we need computed?

Rule:


8. HTML File (index.html)


<!DOCTYPE html>
<html>
<head>
  <title>Vuex CDN Example</title>
</head>
<body>

<div id="app">
  <h2>Count: {{ count }}</h2>
  <h3>Double: {{ doubleCount }}</h3>

  <button @click="increment">Increment</button>
  <button @click="decrement">Decrement</button>
  <button @click="incrementAsync">Increment Async</button>
</div>

<script src="https://cdn.jsdelivr.net/npm/vue@2"></script>
<script src="https://cdn.jsdelivr.net/npm/vuex@3"></script>

<script src="store.js"></script>
<script src="app.js"></script>

</body>
</html>


9. Practice Task for Students

  1. Add a reset mutation
  2. Create a getter for triple count
  3. Add a button to reset count

10. Key Points to Emphasize


11. What NOT to Do (Common Mistakes)

  1. Directly modifying state
this.$store.state.count++

Wrong because it breaks Vuex flow

  1. Using data() for shared state
data() {
  return { count: 0 }
}

Leads back to props/emit problem

  1. Using commit for async logic
setTimeout(() => {
  this.$store.commit('increment')
}, 1000)

Async logic should go inside actions

  1. Skipping computed for UI

Keep it simple:


12. Summary

Vuex provides a centralized store for managing application state. It ensures predictable state changes using mutations and supports async logic using actions. It is especially useful when multiple components need access to shared data.


13. Example: Multiple Components Using the Same Store (Use this for practice)

This example shows how two components read and update the same Vuex state. When one component updates count, the other component also updates automatically.

index.html

<div id="app">
  <counter-a></counter-a>
  <counter-b></counter-b>
</div>

store.js

const store = new Vuex.Store({
  state: {
    count: 0
  },

  getters: {
    doubleCount(state) {
      return state.count * 2
    }
  },

  mutations: {
    increment(state) {
      state.count++
    },
    decrement(state) {
      state.count--
    }
  },

  actions: {
    incrementAsync({ commit }) {
      setTimeout(() => {
        commit('increment')
      }, 1000)
    }
  }
})

app.js


Vue.component('counter-a', {
  template: `
    <div>
      <h3>Component A</h3>
      <p>Count: {{ count }}</p>
      <button @click="inc">+1 from A</button>
    </div>
  `,
  computed: {
    count() {
      return this.$store.state.count
    }
  },
  methods: {
    inc() {
      this.$store.commit('increment')
    }
  }
})

Vue.component('counter-b', {
  template: `
    <div>
      <h3>Component B</h3>
      <p>Count: {{ count }}</p>
      <p>Double: {{ doubleCount }}</p>
      <button @click="dec">-1 from B</button>
      <button @click="incAsync">+1 Async from B</button>
    </div>
  `,
  computed: {
    count() {
      return this.$store.state.count
    },
    doubleCount() {
      return this.$store.getters.doubleCount
    }
  },
  methods: {
    dec() {
      this.$store.commit('decrement')
    },
    incAsync() {
      this.$store.dispatch('incrementAsync')
    }
  }
})

new Vue({
  el: '#app',
  store
})

What you should observe