When looking at Vuex tutorials, you can see most of them are quite simple.
The logic is explained well, but scalability suffers. How will this work in my production app?
Here’s a simple store example from Vuex official docs:
There’s no need to explain this. I assume that you already have some Vue and Vuex knowledge prior to this article.
My goal is not to explain what a store, state, or mutations are.
Instead, I want to show you a massive store with 1,000+ state attributes, mutations, actions, and getters.
I want to teach you how to structure the store for the best maintainability, readability, and reusability.
It can have 100,000+ attributes. It would still be clear.
Let’s dive in.
Meet Modules
As we already said, keeping everything in one file will create a mess. You don’t want a 50,000+ LOC file. It’s the same as keeping your app in one component.
Vuex helps us here by dividing the store into modules.
For the purpose of this example, I will create a store with two modules. Note that the process is the same for 100+ modules, as well as 100+ actions, getters, and mutations within every module.
The namespaced
attribute is incredibly important here. Without it, actions, mutations, and getters would still be registered at the global namespace.
With the namespaced
attribute set to true, we divide actions, mutations, and getters into the modules as well.
This is really helpful if you have two actions with the same name. Having them in a global namespace would create clashes.
As you can see, the module is completely “local” right now. We can access it only through the user object on the state.
Exactly what we want for our massive application.
Cool, now we have a store divided into modules!
However, I don’t like the hardcoded strings for actions. It’s definitely not maintainable. Let’s address this issue.
Types To Save You From Headaches
We don’t just want to access every property from every module in every file. That sentence sounds like hell.
We want to import them first. Then use mapGetters
, mapActions
, or mapMutations
to achieve that.
This gives you a clear view of store attributes used by your Vue file.
But that’s not enough. Everything is still in one file. Let’s see what we can do to scale it properly.
Folder Structure
Ideally, we want to split modules into different folders. Within those modules, we want to split their mutations, actions, getters, state attributes, and types across different files.
Folder store
will be created in the root folder of our project.
It will contain two things:
-
index.js
file -
modules
folder
Before explaining the index.js
file, let’s see how we divide a single module. Let’s check the user
module.
All of its actions, mutations, and getters should be listed in the types.js
file. So, something like:
We’ll have a clear view by importing those consts every time we want to use them.
Let’s look at the actions now. We want to move them to the actions.js
file.
To do so, we only need to copy the actions
object within the module and export default
it, while importing the types:
We will do the same thing for mutations and getters. The state attributes will remain in index.js
(within the user module folder):
Now we have all of our modules divided into multiple files.
The one thing remaining is to link all those modules in the index.js
file within the store
folder:
Conclusion
By using this architecture, we had zero problems with scalability in our massive production app.
Everything is so easy to find.
We know exactly where all the actions are triggered.
The system is highly maintainable.
If you have any recommendations for the improvements, please let me know. I would love to hear your opinion.
Top comments (21)
I like this, we need more posts like this in DEV. Thank you for sharing!
Glad to hear that! More content is coming 🙂
Are you sure we don't need more Dark Mode posts instead?? 😂
But in all seriousness, this is exactly what DEV needs. Solid post @domagojvidovic 👏
Ahh dark mode, more like "dark more!"
Built a large application with 4 modules, after 2 years of working on it I an say in full confidence that if you need modules you might as well just build a seperate application.
In no way did it create any advantages to have it all as one application. Even for shared components ad we eventually moved to a shared design system.
But building a new application sometimes isn't an option...
How does
mapActions
know about the namespacing? Wouldn't identically named actions/mutations still cause problems when called from a component?Easy
Awesome - I use
createNamespacedHelpers
but I guess I never realized that worked the same way as usingmapActions
directly with a namespace as the first parameter!I like having strong types for my actions/mutations without having to import something, so I create an
action
function that uses JS Doc type unionsIn my SFCs I can use it like this:
The value I can pass to
dataAction
is strongly typed to be only one of the keys ofactionTypes
.Never used Vuex Modules before but NuxtJS provides a way through subdirectories I believe, I wonder if it uses this under the hood
This is exactly what Nuxt is doing under the hood.
Hmm, I'm not sure that it's connected
I tried to make it in a small tutorial project on my own.
I got an error: "[nuxt] store/index.js should export a method that returns a Vuex instance. "
Maybe I'm a bit to stupid for it, but in /store/index.js is a function called "export default new Vuex.Store({...})
That's the exported method to return a Vuex instance...
That's so difficult for now :-(
This is fantastic. Thanks :)
I didn't know about this and have been putting everything in one file. I'll definitely be using this technique in the future.
Glad you like it! Start as soon as possible :) let's tackle that tech debt!
Love this. It would have helped me greatly when we were setting up our apps.
Thanks a lot mate! We need more tutorials/guides about scaling a huge system, not just the basics!
The first Vue Apps I built at work I didn't know about modules and the stores definitely got a bit out of hand 😅
I can imagine that chaos 😆
I'm still learning js and vue. I love posts like this to learn best practice from the beginning.
Yep, it's good to create as low tech debt as possible!