Awwwards
How to Use Vuex in a Laravel Spark Project's Hero Image

How to Use Vuex in a Laravel Spark Project

With the release of Vue.js 2.0 and Spark 3.0, the time had come for us to update our own Spark application. In doing so, we ran into the usual headaches and snags that you might expect from such a comprehensive upgrade. But, perhaps the biggest issue for us was Vue's deprecation of $dispatches and $broadcasts for eventing. Here we had a choice. We could either create our own centralized event hub or use Vue's dedicated state manager, Vuex.

Our Vue Point

We decided to go with Vuex for a number of reasons. First, a simple event hub would quickly become unwieldy for us to use due to the complexity of our frontend components. Second, Vuex's ability to create state Modules appealed to us because Spark isn't a single page app. So, we needed a way to keep the various states of our application separate. Lastly, using the event hub didn't seem like the Vue way to solve this problem. When we looked into some of the decisions made for Vue 2.0, we realized that the dev team had worked really hard to strip Vue down to its essence. In doing so, they moved "extra" functionality into other packages, relied on 3rd party providers, or even removed it from the ecosystem entirely (looking at you vue-resource). With this in mind, we wanted to make sure that all of the work we were doing for the migration would stay current, and so Vuex made the most sense for state management.

Once we were comfortable with our decision, it was time to hit the codebase! Now, let me show you how to add Vuex to your Spark project and give you a look at how we decided to organize everything.

Focusing Your Vue

Before we begin, make sure that you are using Spark 3.0 with Vue 2.0 already installed.

First things first, we need to install Vuex. If you check out the Vuex docs, you can see that you simply need to run npm install vuex to add it to your project. With that done, head on over to your resources/assets/js directory and open up app.js. This is where you can import your Vuex store once it has been set up. How you want to set it up depends on your project's directory structure, but ours looks like this:

As you can see, we've created our own vuex directory within resources/assets/js. Inside of this we have our store.js and two other directories, which you can ignore. Returning to our app.js file, you can now see that we've added an import from our vuex/store.js and then added that import to Spark's global Vue instance. This makes the Vuex store available to all Vue components.

With your Vuex store imported correctly, you can now set up your store. Inside of store.js you will need to set import Vue and Vuex at the top of the file like this:

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

Vue.use(Vuex);

Now you can create your global Vuex store. If you're unfamilar with using Vuex, I highly recommend checking out their documentation and getting a feel for how to use it. But, at a very high level, Vuex works like this:

Image provided via the Vuex Documentation

So, your Vue components "dispatch" Actions to Vuex. The Vuex Actions then handle any kind of asynchronous activity before "committing" a Mutation. The Mutations then "mutate" the Vuex State and then everything is rendered back to your Vue components via Getters. This ensures that there is a one-way flow of data throughout your Vue application.

We represent all of this inside of our store.js like so:

This may look confusing, but let's break it down section by section. At the top, we have our import statements, which we covered already. After that, we've defined four constants - one for each of the Vuex patterns. Inside of our state constant, we've defined the initial state of the store with a simple count set to 0. For me, I like to think of this as analogous to a Vue component's data. Next, we have our getters constant. Here we're actually modifying our state by returning the count plus 1. This getter will be used to make the countPlusOne available to our Vue components. Moving along, we reach our mutations constant. Here we're just saying that when the INCREMENT mutation is "committed", then the count state should increment by one. Then we have our actions constant. This increment action can now be "dispatched" from our Vue components to "commit" the INCREMENT mutation. Finally, we export the Vuex.store so that it can be imported into app.js and added to Spark's global Vue instance.

Now that our state has been set up, let's hop into a Vue component and get it interacting with the state. As you can see below, we've set up a pretty simple Vue component that will increment the count and display it.

This looks like a pretty typical Vue component, except we're doing three things to communicate with the Vuex state. First, we've imported mapGetters and mapActions from our store.js. Second, in our computed properties, we have mapped the Getters from our state to our component. Notice that by using Object syntax, I was able to rename the countPlusOne to simply count. Lastly, we've mapped the Actions in the component's method property so I can use increment when I click the button.

Admiring the Vue With DevTools

Putting this all together, let's see how it renders on the page.

As you can expect, we have a simple button next to the count. Let's hop into our Vue devtools and see what the component and state look like there.

In our templates component, we can see that the computed property count is equal to 1, which is exactly what the countPlusOne Getter was supposed to show us. Hopping over to the Vuex store, we can see that the Base State is simply the count equal to 0, which is exactly what we defined in our store.js.

Now let's see what happens when I click the button. As we can see in our Vuex store, clicking the button triggered the increment method that we mapped from the Vuex Actions. This in turn "committed" a Mutation, which we can see here in the Vuex store:

Our count state is now equal to one, which means that our computed property that we mapped from our Getters is now equal to 2.

Finally, we can see that our rendered Vue component has updated as well.

Whew! That was quite a bit of work just to get an incrementer going. But, think of how much more difficult it would be to share the count with your other components in Vue 1.0. You'd have to $dispatch a message onclick up to a parent component, which was listening to it in its event property. Then the parent would have to $broadcast that to all of the other components that are listening for it. And then what do you do if those components have to send events now that the count has updated? It all gets really complicated pretty quickly. But with Vuex, all we have to do in our other components is import the Getters and Actions, and then they can use the state as they see fit and it all stays synchronized. No more crazy paths of $dispatches and $broadcasts to follow!

In ReVue

There's certainly a lot more you can do with Vuex that we didn't cover here. You can create Modules to keep your states separated across your application, or you can fire API calls in your Actions to ensure your database keeps in sync with your application state. In future blog posts, we'll definitely cover these topics and more, but I hope that this post can get you up and running with Vuex and Spark in no 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.