Weekend Project (Part 3): Scaffolding a Vue App with vue-cli and Managing State with Vuex
If you’re just now tuning in, be sure to read Part 1 and Part 2.
Everyone keeps talking about all of these Javascript frameworks lately — Angular, React, Vue… there are just too many options. Taking on this project, I decided to just pick one and roll with it. I initially heard about Vue.js on the Does Not Compute podcast. The hosts, Paul Straw and Sean Washington, spoke highly of it, so I decided to check it out. I’ve looked into Angular and React before (having done some React work recently) and after minutes of reading the Vue documentation and examples, I fell in love. It’s simple, clean, and has a very gentle learning curve, with being rather performant being a nice bonus.
There are multiple ways to write Vue code, including traditional Javascript files (.js) using ECMAScript 5(ES5). The path we’ll take in this post is my personal favorite, writing Vue templates (.vue) using ECMAScript6 (ES6). This method allows us to write our components as individual files consisting of three core components — template, script, and style — and provides us with a file encapsulating everything our component needs.
Prep Work
Before we dive into writing some code, there’s a few things to install… Please follow the install notes for each of these tools before proceeding.
- Node/NPM (I like to use nvm to install Node and NPM)
- vue-cli*
- The websocket server from Part 2 of this blog
Edit: This post is using vue-cli 2.5.16
. I’d now recommend using @vue/cli 3.0
beta. It’s greatly improved and makes the cli a very powerful tool.
Getting Started
Edit (03/27/2019): The Vue ecosystem is always changing, and vue-cli 3 is very intuitive and brings so many great new features to initializing a Vue project. I recommend checking out the docs here.
To get our project rolling, the quickest and easiest way is to use vue-cli, which scaffolds a project for us to include a “hello world” vue project using one of many prebuilt templates. I’m a fan of webpack, so we’ll use the vue-cli webpack template.
In your terminal, run the following command:
$ vue init webpack [project-name]
This command will walk you through a few questions about your project. You can usually use the defaults, but for the sake of brevity we’ll answer “N” to a few questions — Use ESLint, unit tests, and e2e tests. I typically recommend using these, but for this tutorial we won’t need them.
Vue CLI Scaffold
Taking a look at the output of the scaffold, we see that it created the package.json for NPM, the bulky node_modules folder, an index.html, and a bunch of other folders. Let’s go over the folders it created:
- build — This contains a build script and a modularized webpack config, we can leave this as-is for now.
- config — More webpack config templates for different build environments.
- src — This is where our front-end Vue.js code will go. It contains a couple of subdirectories for storing aspects of the Vue ecosystem, such as components, routes, and additional assets.
- static — A folder to store static assets, such as images and font files.
Without touching any of the code, we can start the Vue app using the webpack dev server by running the following command.
npm run dev
This starts a hot-reloading development server at http://localhost:8080.
The command we just ran isn’t some sort of magic, it’s a script predefined by vue-cli in the package.json. NPM allows us to write scripts, or tasks, to run for assigned commands. The dev
command simply runs our webpack dev server. The other command we’ll be concerned with is the build
command, which compiles our front-end code using webpack into a designated directory.
Vue Basics
Now that we have some scaffolded Vue code, we’re ready to start creating our own components and building out our app. Rather than boring you with a Vue how-to, I highly recommend checking out Vue’s official guide to get spun-up on the basics of the framework. From personal experience, I found Vue had a very small learning curve and their guide was comprehensible and easy to digest. Their documentation is world-class and some of the most comprehensive docs I’ve dealt with.
Once you’re rock solid with the Vue fundamentals, we can tackle some of the more complex aspects of the framework and its ecosystem. These additional modules will greatly enhance the capabilities of your project, such as providing client-side routing and centralized state management for larger projects.
Centralized State Management with Vuex
When working with a complex Vue application with multiple components, it is rather common for components to have a need to share data. Without Vuex, we would have to pass data down as props, which are immutable by the child component. To alter the data on the parent component, we’d have to $emit
an event containing the updated data, as well as handle the event from the parent. With a small application where we need to do this once or twice, that’s fine. But with a larger application this gets messy real fast. It’s okay though, Vuex is here to help you out!
Shared State with Vuex
Shared state sounds simple, the pieces of your application that are needed globally are all stored in one location, almost like having a global variable. However, it’s not as simple as this. Vuex consists of four core components:
- State — the single source of truth for our application’s state. State is immutable.
- Getters — computed state based on store state. Similar to computed props.
- Mutations — the only true way to alter state. Mutations cannot be called directly, but must by activated by calling
store.commit([mutation])
. Mutations must be synchronous. - Actions — similar to mutations, except actions commit mutations and can contain asynchronous operations.
For more technical details on the various aspects of the Vuex state management architecture, refer to their official documentation.
Creating the Store
The creation of the store is relatively simple now that you understand the core concepts of the store. Rather than trying to explain Vuex using the actual codenames game code, we will create a basic store that will manage a counter. This is much more comprehensive and allows me to cover more aspects of Vuex.
After creating the store, we want to tell Vue to use it for its state management. This is achieved by simply importing the store and adding it to the main Vue instance, the same way you’d integrate the Vue router.
Using Vuex Within Components
Now that our store is configured, we’ll want to use this within a Vue component to increment and reset the counter. To do so, we can simply access our global store from the variable this.$store
. However, this can become unwieldy when attempting to access multiple aspects of our Vuex instance. Luckily, Vuex provides some helper methods for attaching pieces from the Vuex store to our components. This allows us to access our count by using this.count
rather than this.$store.state.count
. Additionally, it allows us to only attach the pieces from our store that the specific component requires. Each Vuex piece has a helper, as described below, and are each commonly used with the “spread” operator ...
which explodes the object that gets returned, allowing us to essentially merge it with our existing object without any additional libraries or utilities.
- State —
mapState
in thecompute
block of the component. - Getters —
mapGetters
in thecompute
block of the component. - Mutations —
mapMutations
in themethods
block of the component. - Actions —
mapActions
in themethods
block of the component.
Below, let’s compare the Counter component with and without the helpers.
Take note of the computed and methods blocks of the component. In the above example, we create individual functions that simply return the state, getters, mutations, and actions using the longhand form such as this.$store.state.count
. This adds unnecessary bloat to the component.
Using the helpers is made incredibly simple with the spread operator, and it allows us to use the Vuex elements in conjunction with our own computed properties and methods, as shown above. We can import the doubleup
getter while also creating our own custom property, halfCount
.
Conclusion
Vuex is a very useful and powerful component of the Vue ecosystem, and is very simple to integrate into applications of any size. However, each use case is different and not all applications require the additional overhead and complexity. Take this knowledge with you and use it on your next project to avoid ending up with prop passing spaghetti. Stay tuned for the next segment on integrating Socket.io into the Vuex store!
Update 2018/04/24 — Learn how to integrate websockets into your project using Socket.IO and Vuex in Part 4 of this series!