Check out my books on Amazon at https://www.amazon.com/John-Au-Yeung/e/B08FT5NT62
Subscribe to my email list now at http://jauyeung.net/subscribe/
Vuex 4 is in beta and it’s subject to change.
Vuex is a popular state management library for Vue.
Vuex 4 is the version that’s made to work with Vue 3.
In this article, we’ll look at how to use Vuex 4 with Vue 3.
Modules
We can divide our Vuex 4 store into modules so that we can divide our states into multiple smaller parts.
Each module has their own state, mutations, actions, and getters.
Module Local State
Each module has their own local state. We can map the module’s state into computed properties and mutations into methods by writing:
<!DOCTYPE html>
<html lang="en">
<head>
<script src="https://unpkg.com/vue@next"></script>
<script src="https://unpkg.com/vuex@4.0.0-beta.4/dist/vuex.global.js"></script>
<title>App</title>
</head>
<body>
<div id="app">
<button @click="increment">increment</button>
<p>{{doubleCount}}</p>
</div>
<script>
const moduleA = {
state: () => ({
count: 0
}),
mutations: {
increment(state) {
state.count++;
}
},
getters: {
doubleCount(state) {
return state.count * 2;
}
}
};
const store = new Vuex.Store({
modules: {
moduleA
}
});
const app = Vue.createApp({
methods: {
...Vuex.mapMutations(["increment"])
},
computed: {
...Vuex.mapGetters(["doubleCount"])
}
});
app.use(store);
app.mount("#app");
</script>
</body>
</html>
We created the store with the modules
property to include our module in the store.
Then in our Vue instance, we have the mapMutations
and mapGetters
methods to map the mutations to methods and the getters to computed properties.
We can do the same with actions and states.
For example, we can write:
<!DOCTYPE html>
<html lang="en">
<head>
<script src="https://unpkg.com/vue@next"></script>
<script src="https://unpkg.com/vuex@4.0.0-beta.4/dist/vuex.global.js"></script>
<title>App</title>
</head>
<body>
<div id="app">
<button @click="increment">increment</button>
<p>{{moduleA.count}}</p>
</div>
<script>
const moduleA = {
state: () => ({
count: 0
}),
mutations: {
increment(state) {
state.count++;
}
},
actions: {
increment({ commit }) {
commit("increment");
}
}
};
const store = new Vuex.Store({
modules: {
moduleA
}
});
const app = Vue.createApp({
methods: {
...Vuex.mapActions(["increment"])
},
computed: {
...Vuex.mapState({ moduleA: (state) => state.moduleA })
}
});
app.use(store);
app.mount("#app");
</script>
</body>
</html>
We called the mapActions
method with an array of action names.
The mapState
method is called with an object with methods to return the module states.
The states are in the object with the module name as the property name.
Namespacing
We can namespace our modules.
This way, we won’t have everything under one global namespace.
Also, this lets multiple modules react to the same mutation or action type.
For instance, we can write:
<!DOCTYPE html>
<html lang="en">
<head>
<script src="https://unpkg.com/vue@next"></script>
<script src="https://unpkg.com/vuex@4.0.0-beta.4/dist/vuex.global.js"></script>
<title>App</title>
</head>
<body>
<div id="app">
<button @click="this['moduleA/increment']">increment</button>
<p>{{moduleA.count}}</p>
</div>
<script>
const moduleA = {
state: () => ({
count: 0
}),
mutations: {
increment(state) {
state.count++;
}
},
actions: {
increment({ commit }) {
commit("increment");
}
}
};
const store = new Vuex.Store({
modules: {
moduleA: {
namespaced: true,
...moduleA
}
}
});
const app = Vue.createApp({
methods: {
...Vuex.mapActions(["moduleA/increment"])
},
computed: {
...Vuex.mapState({ moduleA: (state) => state.moduleA })
}
});
app.use(store);
app.mount("#app");
</script>
</body>
</html>
We mapped the action with the namespace name since we set the namespaced
property to true
.
We can do the same with getters and mutations.
To do that, we write:
<!DOCTYPE html>
<html lang="en">
<head>
<script src="https://unpkg.com/vue@next"></script>
<script src="https://unpkg.com/vuex@4.0.0-beta.4/dist/vuex.global.js"></script>
<title>App</title>
</head>
<body>
<div id="app">
<button @click="this['moduleA/increment']">increment</button>
<p>{{this['moduleA/doubleCount']}}</p>
</div>
<script>
const moduleA = {
state: () => ({
count: 0
}),
mutations: {
increment(state) {
state.count++;
}
},
getters: {
doubleCount(state) {
return state.count;
}
}
};
const store = new Vuex.Store({
modules: {
moduleA: {
namespaced: true,
...moduleA
}
}
});
const app = Vue.createApp({
methods: {
...Vuex.mapMutations(["moduleA/increment"])
},
computed: {
...Vuex.mapGetters(["moduleA/doubleCount"])
}
});
app.use(store);
app.mount("#app");
</script>
</body>
</html>
We called mapMutations
and mapGetters
with the namespace name since we set namespaced
to true
in the module.
Conclusion
We can create our modules and namespace them so that we can have multiple smaller parts of a store that manage their own state.