TL;DR — This article will show you how to get quickly started with Vue and Vuex. In addition to grabbing data from an API and displaying it.
We will be building a simple application that searches the Marvel database for characters then displays the results.
What is Vue?
Vue (pronounced /vjuː/, like view) is a progressive framework for building user interfaces. Unlike other monolithic frameworks, Vue is designed from the ground up to be incrementally adoptable. The core library is focused on the view layer only, and is easy to pick up and integrate with other libraries or existing projects. (https://vuejs.org/v2/guide/)
What is Vuex?
Vuex is a state management pattern + library for Vue.js applications. It serves as a centralized store for all the components in an application, with rules ensuring that the state can only be mutated in a predictable fashion. (https://vuex.vuejs.org/en/intro.html)
What is VuePack?
Vuepack is a starter kit which uses Vue 2, Vuex, Vue-router and Webpack 2. It essentially bootstraps and configures your project for you with these technologies.
Getting Started
The application we are implementing will search the Marvel database (via API) for characters based on user input and then display the results.
Marvel API
First, you will need a Marvel API key, so go over to the Marvel Developer Portal and sign up for an API key.
For the API calls, you can either directly call the API from http://gateway.marvel.com/v1/public or use a Node server as a proxy. However, if you are calling it directly then you will have to deal with Cross-Origin Resource Sharing (CORS). For this article, I will be using my own Marvel Power Levels API, written in Node and Express.
VuePack
We will be creating our Vue.js project with VuePack using the vue-cli. The vue-cli allows you to use different open source templates which will get you up and running without all the hassle of configurations.
$ npm install -g vue-cli $ vue init egoist/vuepack new-project $ cd new-project $ npm install
Use the default options for the scaffolding (vue init):
? Do you want to use ESLint? Yes ? Generate components in JSX format? No ? Support Electron? No ? Add testcafe to run integration tests? No
Axios for API calls
We will be using Axios for our API calls.
$ npm install axios --save
Semantic UI
Finally, we will be using Semantic UI as our CSS framework.
Inside build/index.html
Start Developing
To run the application and start developing:
$ npm run dev
Open up your browser to localhost:4000
. You should now see the default counter that is built in with the application.
Inside store/index.js
we will be removing the default counter store code and setting up our own store for Marvel characters.
Replace the code with the following:
In our store, we have 4 different parts that make up the store.
State
The state refers to an object where we define the data for our application structure. This data is to be used throughout our components, also known as the “single source of truth”.
const state = { data: [] }
For our application, we will only have 1 state which is data. data
will contain our character data that is returned from the Marvel API.
Mutations
Mutations are where we write events that update the store. Mutation events are also the only way to update the store. A mutation has a string type and a handler function.
const mutations = { RECEIVE_CHARACTERS (state, { characters }) { state.data = characters } }
In our application, the type is RECEIVE_CHARACTERS
and the handler function updates the state’s data array with the payload that is passed in.
Actions
Actions are where we write functions that will commit changes to the store. Instead of mutating the store’s state directly, actions commit mutations, which then updates the state. Actions are called with a dispatch call, ex: this.$store.dispatch('FETCH_CHARACTERS')
const actions = { async FETCH_CHARACTERS ({ commit }, name) { const url = `http://localhost:8080/api/characters?limit=12&name=${name}` const { data } = await axios.get(url) commit('RECEIVE_CHARACTERS', { characters: data.results }) } }
Additionally, actions can be asynchronous so this is where we will put our API calls.
In our action FETCH_CHARACTERS
, we are doing an asynchronous call to the API using Axios. Then when the async call is finished, we do a commit to the RECEIVE_CHARACTERS
mutation with the API data results.
Getters
Getters are helper functions that retrieve data from our state.
const getters = { characters: state => { return state.data.map(data => { return { name: data.name, url: data.urls[1] ? data.urls[1].url : data.urls[0].url, image: `${data.thumbnail.path}.${data.thumbnail.extension}`, description: data.description === '' ? 'No description listed for this character.' : data.description } }) } }
In our application, we are using a getter to take the computed data and return a simplified object that we can use. The reason to use a getter here is because sometimes an API will return data we do not need or the data that we do need is deeply nested.
Special Mention: Modules
We are not using modules inside our application because they are a better fit for larger applications with multiple states.
Due to using a single state tree, all state of our application is contained inside one big object. However, as our application grows in scale, the store can get really bloated.
To help with that, Vuex allows us to divide our store into modules. Each module can contain its own state, mutations, actions, getters, and even nested modules — it’s fractal all the way down. (https://vuex.vuejs.org/en/modules.html)
Creating Components
Now that we have created our store and understand the core concepts of the Vuex store, we can now create components that interact with the store.
SearchCharacterForm Component
The first component we will be creating is the SearchCharacterForm. This component will contain our template and logic for the search textbox.
Inside client/components
, create a new file called SearchCharacterForm.vue
.
Add this code to SearchCharacterForm.vue
:
The main things to note here are the data and methods.
Data is used to define the properties that will be added to Vue’s reactivity system. This means that when the value of the property changes, the view will “react”, updating to match the new values and vice-versa. Also known as two-way binding. We will be using the name
property to tell our action what Marvel character name to fetch.
Methods are used to define functions that will be used for any event handlers inside the template. In our application, we created the handleSearch
method which dispatches an action to fetch characters. The fetch characters action will then update the store’s data state with the API results.
CharactersList Component
To show the results from the search textbox, we will need to create a CharactersList component. This component will contain our template and logic to display the characters data from our store.
Inside client/components
, create a new file called CharactersList.vue
.
Add this code to the CharactersList.vue
:
The component will display and render the search results from our store’s state. In this component, we use the characters
getter that we created earlier in our store file. Using a getter allows us to retrieve and use our state data in a clean and minimal way instead of doing a lot of data manipulation to get what we want.
Furthermore, we access our characters getter as a computed property because we want our component to always get the current state once it gets altered. So when the user searches a different Marvel character, the list will be updated because our CharactersList component knows to use the latest and current state data.
Displaying the Components
To finish up, we need to add these components in our Home view.
Inside views/Home.vue
, replace the code with:
Conclusion
Going through the whole flow, a character search will dispatch an action that calls the API, and commits a mutation. The mutation then updates the state data, which is then displayed with the CharactersList component that uses the characters getter.
Now, if you run the app with npm run dev
you should be able to see a search form. Searching a valid Marvel character should then display a list of the results.