Awwwards
How To Set Up Modules in Vuex's Hero Image

How To Set Up Modules in Vuex

We're back with another in-depth walkthrough on using Vuex. Last week, we took a look at setting up Vuex in a Spark project. We'll continue where we left off and investigate how to set up Vuex Modules in a Spark project.

Why Use Modules?

Modules are super important to use once your application starts growing. Because Vuex is a single state tree, the entire state of your application is contained inside one big object. However, this can get really bloated as you add new features and their corresponding states. We get around this state bloat by using Modules to separate out the various pieces of state.

Modules are pretty easy to set up and begin using, but they have one big caveat. Since all of the Modules are registered under the global namespace, we'll need to individually namespace each of our Modules to avoid naming conflicts. Now that might sound like a confusing jumble of words, but let's break it down.

Let's say we have two Modules, and they each need to have their own state called currentView. We'd set this up like usual, so both Modules would have a State, Getters, Actions, and Mutations. Now what happens when we set up their currentView? We'd add a currentView property to the Store and initialize it; then we'd set up an Action and Mutation to update the currentView, and then we'd make it available via a Getter. All together, both of your Modules would look like this:

const state = {
	'currentView': 'inital-value'
}

const getters = {
	currentView: state => {
		return state.currentView;
	}
}

const mutations = {
	UPDATE_CURRENT_VIEW (state, value) {
		state.currentView = value;
	}
}

const actions = {
	updateCurrentView (context, value) {
		context.commit('UPDATE_CURRENT_VIEW', value)
	}
}

const module = {
    state,
    getters,
    mutations,
    actions
};

export default module;

Now the problem here is that when you import this in your global store, your Modules share the same names for everything! So if your Actions are mapped to a component and you called updateCurrentView, Vuex wouldn't know which Module to use. Fortunately, Vuex allows us to namespace our modules so we can avoid this issue. Let's take a look at how to do that, and while we're at it I'll explain how to set up Modules in your project as well.

How To Set Up Modules

If you recall in my last post, I had set up my directory structure like so:

Last time, I told you to ignore the modules and utils folder, but they'll be the center of attention this time around. As you can guess, you'll want to include all of your Modules inside the modules directory. In there, I like to make a subdirectory for each module that I have so I can break out their Store, Actions, and Mutations like this:

So instead of having everything inside one file, like we saw in the example from earlier, I can import my Actions and Mutations into my store.js and then just export the entire Module so it can be imported into my global store. That means my typical Module store.js file looks like this:

//resources/assets/js/vuex/modules/yourModuleName/store.js

import actions from './actions.js';
import mutations from './mutations';

const state = {
	'currentView': 'inital-value'
}

const getters = {
	currentView: state => {
		return state.currentView;
	}
}

const module = {
    state,
    getters,
    mutations,
    actions
};

export default module;

You could also import your State and Getters, but I find that they are usually pretty tightly coupled together, so it makes sense to keep them in the same file. Now that we have a Module set up, it's time to import and register it in the global state.

Inside of my vuex directory, I have a top level store.js file. If we weren't using any Modules, this is where your entire application state would belong. But since we are, it basically just becomes a place to register your Modules and include any global state details. To register your Modules, just import them into this store.js file and include them in the modules option in your new Vuex.Store Object, like this:

//resources/assets/js/vuex/store.js

import Vue from 'vue'
import Vuex from 'vuex'

Vue.use(Vuex);

//Separate Module States
import <yourModuleName> from './modules/your-module-name/store'

export default new Vuex.Store({
    modules: {
        yourTemplateName: yourTemplateName
    }
})

Namespacing

Thanks to the awesome efforts of the Vue team, this namespacing section is out-of-date! To see out how to set up namespacing the new way, go here.

Now that our Module is set up, we can take care of the namespacing issue. Inside of your utils directory, you can set up two files called namespace.js and types.js. Inside of namespace.js, we'll set up a helper function provided by jxlarrea on GitHub. The function looks like this:

//resources/assets/js/vuex/utils/namespace.js

function mapValues (obj, f) {
    const res = {};
    Object.keys(obj).forEach(key => {
        res[key] = f(obj[key], key)
    });
    return res
}

export default (module, types) => {
    let newObj = {};

    mapValues(types, (names, type) => {
        newObj[type] = {};
        types[type].forEach(name=> {
            var newKey = module + '/' + name;
            newObj[type][name] = newKey;
        });
    });
    return newObj;
}

This takes care of a bunch of repetitive work for us in our types.js file. In there, we'll actually be defining the namespacing for our Modules, which will look like this:

//resources/assets/js/vuex/utils/types.js

import namespace from './namespace'

//Templates Module
export const yourModuleName = namespace('yourModuleName', {
    getters: [
        'currentView'
    ],
    actions: [
        'updateCurrentView'
    ],
    mutations: [
        'UPDATE_CURRENT_VIEW'
    ]
});

As you can see, we're importing the namespace function to take care of the actual namespacing. This makes types.js much more readable and easy to parse, and it saves us from some unnecessary repetition.

Now to use our namespacing, we need to hop back into resources/assets/js/vuex/modules/yourModuleName/store.js. At the top of the page we need to import our namespace, like so:

import { people } from '../../utils/types.js';

With the namespacing imported, we just have to do a slight refactor to make sure we're referencing it.

const getters = {
	[yourModuleName.getters.currentView]: state => {
		return state.currentView;
	}
}

const mutations = {
	[yourModuleName.mutations.UPDATE_CURRENT_VIEW]: (state, value) => {
		state.currentView = value;
	}
}

const actions = {
	[yourModuleName.actions.updateCurrentView]: (context, value) => {
		context.commit('UPDATE_CURRENT_VIEW', value)
	}
}

With all of this in place, you can now add as many Modules as you like and you'll be sure that they won't run into any namespacing conflicts.

The Wrap Up

Well there you have it, you can now set up Modules and avoid the issue of namespacing. This might not seem like a huge deal if you're used to building smaller apps. But as your applications grow, you'll be happy that all of your states can be managed independently. In my next post, I'll show you how to setup your Actions to fire API calls so your database can stay synchronized with your Vuex. See you next time!


Nick Basile's Profile Picture

Nick Basile


Lead UI/UX Engineer

Nick is the Lead UI/UX Engineer at Metric Loop. He lives in Austin, TX and spends his free time taking long strolls on South Lamar. If you see him, be sure to honk.