Build a shopping cart with Vue 2 and Vuex
In a previous tutorial we went through the basics of setting up a todo application with Vue.js. In this tutorial we will build out a shopping cart application with Vue.js and Vuex. Vuex is a state management library for Vue.js. This means that it helps us keep application state in sync across multiple components and routes through a single source of truth, called a store. In our shopping cart application users will be able to view products, add them to their cart and view the total cost of their order. We will handle routing on the frontend using the official vue-router package.
Vuex is maintained by the Vue.js core team and brands itself as centralized state management for Vue.js. It is similar to Redux or Flux, application development techniques pioneered by Facebook and the React.js community. If you are unfamiliar with these libraries, not to worry! The Vuex library is for maintaining a global Store that knows about all of the state within our application. In the long run this can make development easier because state is modified and collected from one place instead of many. These changes are then propogated down to the individual components primarily through computed properties.
The source code for the Vuex shopping cart application is available here
Vuex adds some additional complexity upfront but in the long term can make our application development easier, cleaner and faster. If you write client side tests for your application having a single state tree through Vuex can also make the application simpler to reason about.
Without Vuex, you could communicate state across components using events and passing properties as in the diagram below. This works well for simple parent to child communication and chances are you already use it in your Vue.js applications!
A more comprehensive approach to state management from the ground up would be to create a store object, attach it to the global namespace and read/write from that.
Vuex builds on top of this starting point and adds new concepts like getters, mutations and actions. Let’s take a look at how it works by building our shopping cart application.
The first step is to use the Vue CLI that is maintained by the core team. We’re going to use the webpack-simple scaffold.
Once you have generated a new application, modify the .babelrc file like so:
This gives us the ability to use the ES6 spread operator in our code. You can read more about the spec here.
Install some packages that we will need for the build and the application.
The babel stuff isn’t strictly required but can make our lives easier down the line. I generally try not to concern myself with webpack and build stuff but in this case it helps.
We’re going to use Bulma for styling so add that with font awesome to index.html.
Now that we have the application generated let’s build out the rest of the shopping cart.
The starting point for our application is in src/main.js.
In this file we add code to register our routes and components. We also attach the store to the global Vue app. If you are unfamiliar with ES6 syntax,
store in an object is the same as
In our main App.vue component we will register the base template for our Vue application complete with the NavBar.
The product and cart pages will render in the
<router-view> element. We can come back to the NavBar a little later, but the full source code for it is available here.
Now that we have our application scaffold set up, create a new folder called src/store that will have an index.js and a mutation-types.js file. The mutation types file defines what mutations can occur within our application:
What is a mutation you ask? Mutations change the state of our application. In this case the one thing our application does is modifies the cart state to feature new products as customers add them. Mutations must be synchronous, so no ajax calls belong in mutations.
We have the mutation-types.js file so that our mutations are defined in one place and we can see at a glance all the mutations to the state our app can possibly make.
In the src/store/index.js define our mutations and register our store:
In the above code we are missing values for
actions. Those are defined below:
To start with are instantiating a global state with three products and setting our cart (
added) to an empty array. If you wanted to load these products in via an ajax call we would require additional mutations to add in products to the state. For the sake of simplicity we are setting up the state with our products statically defined.
Getters are for accessing state values. The official docs define getters as similar to computed properties for stores. In the above code we have a getter to access the products, the number of products and the products within our cart. The
map function creates an array specifically for the
added key that retains information about the quantity of a particular product the customer would like to order.
Now that we have defined our routes and store it’s time to build out the components for our application. In the src/components folder add Cart.vue, Products.vue and NavBar.vue files.
The Products component will render a list of the products for sale and feature “Add to Cart” buttons for customers to buy the products.
mapGetters function pairs keys to the results of the getter functions. This means that in our local component state their are
length values that map to our global store. These values are reactive so that if our state updates within another component these values will also be updates. The
mapActions helper maps local methods to the actions we defined within our store.
We can see the value of this when we define our NavBar. In the NavBar we want to show the number of products the user currently has in their cart. To do this we will read from the same state as the products component. Instead of communicating state and forth between siblings and children they read from a single source of truth, the store.
And the relevant HTML within the Vue template:
itemsInCart value draws its data from the store and the get cartProducts call. We make modifications to the data in our computed property so that we display the current number of items the customer would like to purchase. The classes here add in styling from the Bulma CSS library and the
<router-link> components render anchor tags for navigation.
Finally, we have the Cart component for rendering the items the user would like to buy and the total cost for their order.
Here we use computed properties with the mapGetters helper function to bind the
added value of the store to
products in the local component scope. We read this one to our calculate our
total in a value local to this component but that derives its information from the global store. Lastly, we add a method for checkout that shows an alert for the total amount of money the user owes. If you are interested in building out real life payment processing you can view this tutorial
Vuex is a state management package for Vue.js that handles modifications to the state tree within a single source of truth called aptly the State. The State can only be modified with Mutators which must be syncronous functions. To run asynchronous functions or perform other tasks we can define Actions which can be called from components and ultimately call Mutators. We can access the State values within components through Getter functions. The
mapActions can simplify our component definitions.
Though Vuex brings in many new concepts to Vue.js application development it can be very helpful in managing complex application state. It is not necessary or required for many Vue.js applications but learning knowing about it can make you a better, more productive developer.