Vuex is a state management pattern and library designed for Vue.js applications. It serves as a centralized store for all the components in an application, enforcing rules to ensure that the state can only be mutated in a predictible fashion. This architecture facilitates data sharing and caching across components, solving the problem of prop drilling and event emission complexity.
Project Setup and Configuration
To begin, install the Vuex dependency. Subsequently, create a dedicated directory named store within the src folder, containing an index.js file.
import Vue from 'vue';
import Vuex from 'vuex';
Vue.use(Vuex);
export default new Vuex.Store({
state: {},
mutations: {},
actions: {},
getters: {}
});
Next, inject the store instance into the root Vue component within main.js. This makes the store accessible to all child components.
import Vue from 'vue';
import App from './App.vue';
import store from './store';
new Vue({
render: h => h(App),
store
}).$mount('#app');
Core Concepts
1. State
The State object holds the application-level data. It acts as the "single source of truth".
Defining State:
state: {
counter: 0
}
Accessing State:
Method A: Direct access via this.$store.state.propertyName.
Method B: Using the mapState helper to map store properties to local computed properties.
import { mapState } from 'vuex';
export default {
computed: {
...mapState(['counter'])
}
}
2. Mutations
Mutations are synchronous functions responsible for modifying the state. Direct modification of state outside of mutation handlers is prohibited to ensure traceability.
Defining Mutations:
mutations: {
INCREMENT(state) {
state.counter++;
},
INCREMENT_BY(state, payload) {
state.counter += payload;
}
}
Invoking Mutations:
Method A: Use this.$store.commit('MUTATION_NAME', payload).
Method B: Use mapMutations to map them to local methods.
import { mapMutations } from 'vuex';
export default {
methods: {
...mapMutations(['INCREMENT', 'INCREMENT_BY'])
}
}
3. Actions
Actions handle asynchronous operations, such as API calls. They do not mutate the state directly but commit mutations instead.
Defining Actions:
actions: {
incrementAsync(context) {
setTimeout(() => {
context.commit('INCREMENT');
}, 1000);
},
incrementByAsync(context, payload) {
setTimeout(() => {
context.commit('INCREMENT_BY', payload);
}, 1000);
}
}
Dispatching Actions:
Method A: Trigger via this.$store.dispatch('actionName', payload).
Method B: Use mapActions to map actions to local methods.
import { mapActions } from 'vuex';
export default {
methods: {
...mapActions(['incrementAsync', 'incrementByAsync'])
}
}
4. Getters
Getters function like computed properties for the store. They derive values based on the state, updating automatically when the state changes.
Defining Geters:
getters: {
formattedCounter(state) {
return `Current Count: ${state.counter}`;
}
}
Accessing Getters:
Method A: Access via this.$store.getters.getterName.
Method B: Use mapGetters helper.
import { mapGetters } from 'vuex';
export default {
computed: {
...mapGetters(['formattedCounter'])
}
}
Full Component Integration Example
The following code demonstrates a component integrating all four core concepts:
<template>
<div>
<h2>{{ counter }}</h2>
<p>{{ formattedCounter }}</p>
<button @click="INCREMENT">Add 1</button>
<button @click="INCREMENT_BY(5)">Add 5</button>
<button @click="incrementAsync">Add 1 Async</button>
<button @click="incrementByAsync(10)">Add 10 Async</button>
</div>
</template>
<script>
import { mapState, mapGetters, mapMutations, mapActions } from 'vuex';
export default {
computed: {
...mapState(['counter']),
...mapGetters(['formattedCounter'])
},
methods: {
...mapMutations(['INCREMENT', 'INCREMENT_BY']),
...mapActions(['incrementAsync', 'incrementByAsync'])
}
};
</script>
Modular Architecture
For large-scale applications, Vuex allows splitting the store in to Modules. Each module maintains its own state, mutations, actions, and getters.
const userModule = {
state: { isLoggedIn: false },
mutations: { /* ... */ },
actions: { /* ... */ },
getters: { /* ... */ }
};
const productModule = {
state: { items: [] },
mutations: { /* ... */ },
// ...
};
const store = new Vuex.Store({
modules: {
user: userModule,
product: productModule
}
});
// Accessing nested state
console.log(store.state.user.isLoggedIn);