Vanilla Vue is a great library and you can get a lot done with it. However, one of the most common mistakes I’ve seen is people who aren’t adopting Vuex early on, and have to figure out how to swap in Vuex later. Starting with Vuex isn’t as difficult as it sounds, and if you think you might need it, you should just use it. Here’s some quick warning signs that you should have used Vuex already:
- You’re using
.$rootto access data attributes from children.
- You’re passing nearly your entire data object to child components.
- You’re avoiding using components and building your page as one big Vue instance.
- You’re finding you’re implementing a lot of client side business logic in watch statements.
- You’re considering (or already have made) a global event bus.
It’s not that it’s not possible to build things this way, it’s just much more fragile than it’d be otherwise. Let’s quickly go through switching a simple example to Vuex.
Here’s a simple example with a text field. Let’s make it a tad more complicated and add debouncing Using Underscore.js’s debounce. The HTML is exactly the same through all of these steps:
Here I’ve changed
field to be a computed attribute with getter and setter, and moved where the actual value is stored to
actualField. (In practice, it’d probably make more sense to keep the data attribute consistent and change the HTML, but this is just an example.)
The key take away from this step is that the HTML no longer directly accesses where the data is stored; it doesn’t reference
actualField at all. This means that we can control how changes in the
<input> element are carried over to
actualField. Now let’s move to Vuex:
I created a simple Vuex Store with a
field attribute, and you can see in the computed getter you can access that value as
this.$store.state.field. I created too functions: a mutation
updateField and an action
updateField, and I changed the computed setter to dispatch the action
Both actions and mutations are required by Vuex. Vue components can run actions by using
this.$store.dispatch— they act as the ‘interface’ for your Vuex store, and define the things you can make the store do — hence the name ‘actions’.
Mutations are called within actions using
commit(). A mutation makes actual changes to the Vuex store’s state, and they are meant to be atomic — whatever changes you make within a mutation aren’t propagated out to Vue until after the mutation is complete.
I tend to think of mutations as similar to database transactions — something that’s meant to be as small and simple as it can be. Actions on the other hand should contain the bulk of the complexity. Actions should have nice names that map to user intent, whereas mutations should be named after what they’re doing to the state. Actions are your git porcelain. Mutations are your git plumbing.
Lastly, I can use this same Vuex store for any number of components. Any other component using this store can reference
this.$store.state.field . We’ve separated out where data stored into something we can share across components easily.