You may have already realised that it is quite easy and convenient to use store from different components. Thus, Modules in Vuex are a great supplement to the concept of storing all needed state in the external store. They provide a way to encapsulate related state, mutations, actions, and getters into separate namespaces, making it easier to maintain and scale large-scale applications. And don't be scared, if you've learnt about state, getters, mutations and actions, you're already halfway through getting the benefit of using Vuex modules!
In this guide, we will explore the concept of modules in Vuex, learn how to create and register modules, and understand how they can be used to organize and structure state management logic in Vue.js applications. We will also discuss best practices for module organization and common use cases for leveraging modules in Vuex. Let's dive in and explore the power of modules in Vuex!
Understanding modules in Vuex
Vuex modules are a fundamental feature that enhances the organization and management of state in Vue.js applications. Modules allow developers to logically divide the Vuex store into smaller, reusable units, each responsible for managing a specific domain of state and associated functionality.
One of the key benefits of Vuex modules is their ability to encapsulate related state, mutations, actions, and getters into cohesive units. This modular approach promotes code organization, readability, and maintainability by separating concerns and reducing complexity. By breaking down the store into modules, developers can better understand and rationalise about the state management logic of their applications.
Another advantage of the modules becomes evident when multiple developers are working on different features or sections of an application, using modules allows them to work independently on separate modules without interfering with each other's code.
So, let's summarise the benefits. Scenarios where using Vuex modules is advantageous include:
Large-scale applications: organizing the Vuex store into modules helps maintain a clear structure and makes it easier to manage and scale the application over time.
Team collaboration: by dividing the store into modules, teams can work on different modules independently without risking conflicts or inadvertently affecting other parts of the application.
Reusability: Vuex modules can be reused across different parts of the application or even in multiple applications, promoting code reuse and reducing development effort.
Separation of concerns: modules enable developers to separate different concerns of the application, such as user authentication, data management, or UI components, into distinct modules, leading to cleaner and more maintainable code.
Creating modules in Vuex
In Vuex, modules help keep our code neat and organized by breaking it into smaller parts. Now we're going to go over all the steps of how to set up modules, making it easier to manage your store. Plus, we'll include an example to help you see how it all fits together!
Consider setting up a shopping cart. It provides functionality to add and remove items to and from the cart.
At first, think of defining module structure: each module should have its own state, mutations, actions, and getters. Decide on the boundaries and responsibilities of each module. After you figured out the amount of required modules, pass its names when creating a store to the modules object. For our example, it's one module of a shopping cart:
// index.js
import { cart } from './modules/cart'
export default createStore({
modules: {
cart,
// Other modules can be added here
}
})The next thing we should think about is what states, mutations, actions and getters we will export from the cart.js module. Let's start with the state and getters object:
// cart.js
const state = {
// Define the initial state for the module in the state object
items: [],
}
const getters = {
// Define getter functions that take state as an argument and return the computed value
getCartItems: (state, rootState) => {
return state.items.map(({ id, quantity }) => {
const item = rootState.items.all.find(item => item.id === id)
return {
id: item.id,
title: item.title,
price: item.price,
quantity
}
})
}
}Now, we'd like to add mutations and actions with functionality for adding and removing items.
const mutations = {
addItem(state, item) {
state.items.push(item);
},
removeItem(state, index) {
state.items = state.items.filter((item) => item.id !== index);
},
};
const actions = {
addToCart({ commit }, item) {
commit('addItem', item);
},
removeFromCart({ commit }, index) {
commit('removeItem', index);
},
};Export the module, and we can move on to the next step:
export const cart = {
state,
getters,
mutations,
actions,
};Accessing modules in Vue3 components
Before we turn to the component to access module fields, it's essential to understand the namespaced field in the module object. It allows you to organize module-specific functionality within a namespace. This namespace prevents naming conflicts with other modules in the store. When set to true, actions, mutations, and getters are automatically prefixed with the module's namespace.
For example, if a module named cart has namespaced: true, its actions, mutations, and getters are accessed as cart/actionName, cart/mutationName, and cart/getterName, respectively. This field is listed as a module field along with state, getters, mutations and actions:
export const yourModule = {
state,
getters,
mutations,
actions,
namespaced: true
}To access different parts of the store, you use properties of store object such as state for accessing state, getters for accessing getters, dispatch for dispatching actions, and commit for committing mutations. Let's access them from a cart module in our App.vue component.
<script setup>
import { computed } from 'vue';
import { useStore } from 'vuex';
const store = useStore();
// Access state
const items = computed(() => store.state.cart.items);
// Access getters
const cartItems = computed(() => store.getters['cart/getCartItems']);
// Access actions
const addToCart = (item) => store.dispatch('cart/addToCart', item);
const removeFromCart = (item) => store.dispatch('cart/removeFromCart', item)
// Access mutations
const incrementQuantity = () => store.commit('cart/incrementQuantity')
</script>
<template>
<ul>Items:
<li v-for="item in items" :key="item.id" :value="item.id">
{{ item.title }}
<button @click="addToCart(item)">
Add to cart
</button>
<button @click="incrementQuantity()">
Plus one
</button>
<button :disabled="!cartItems.length" @click="removeFromCart(item)">
Remove from cart
</button>
</li>
</ul>
</template>That's the way you use your functionality from a module. Let's recap the steps taken:
import necessary functions from
vueandvuex,don't forget to declare a store with imported from Vuex
useStorefunction,declare variables and bind
statesandgettersviacomputedfunction,define methods which
dispatchactions andcommitmutations.
Conclusion
Vuex modules provide a powerful way to organize and manage state in Vue.js applications. By breaking down the state management logic into smaller, reusable units, modules help maintain a clean and scalable codebase. They allow developers to encapsulate related functionality, making it easier to reason about and maintain the application's state.
In conclusion, remember what you learned today:
Modules are being registered inside
createStorefunction as fields of amoduleobject.Its properties can be accessed using
storeobject.Namespacing allows you to avoid naming conflicts between different modules, ensuring a clean and predictable codebase.
Keep your modules focused and cohesive, and aim to create reusable modules that can be easily plugged into different parts of your application.