Vue.js is a great Javascript frontend framework and its documentation is very clear and straight to the point. You can either choose to integrate Vue into an already existing app (the JQuery way) or build a Webpack based Single Page Application (SPA) in the React.js way. Let’s build a basic SPA here connecting to a REST API backend using Node.js, Webpack, Vue Loader, Vue Router, and Axios. Setting up such a project is not that easy in my opinion, so here is a short memo about how to do it on Ubuntu. For your information, here is why we’re using Webpack.
Set Up a Vue.js Project
Install Node.js and npm.
Then use npm to install vue-cli and use vue-cli to install the vue-loader loader for Webpack that enables you to use Vue components mixing HTML, CSS, and JS in a single .vue file. It also installs the whole ecosystem needed for a SPA like vue-router. Last but not list, install Axios we’ll be using for API fetching.
sudo npm install -g vue-cli
vue init webpack vue_project # Answer various questions here
cd vue_project
npm install
npm install --save axios
More information about the project’s structure here.
Workflow
Dev
Dev is made dead easy thanks to the local web server and the hot reloading feature (web page is modified on the fly when you change source code). Just run:
npm run dev
and start coding.
Note: I personnaly had to face a bug so that hot reloading was broken because of a permission problem (I’m running Ubuntu 17.10). I fixed it thanks to the following command:
echo 100000 | sudo tee /proc/sys/fs/inotify/max_user_watches
Deployment
Once you need to push your app to production run:
npm run build
and your app is now compiled into the dist
directory. It’s up to you to decide how you want to deploy it. Personally I’m putting my app into a Docker container running Nginx and make Nginx point to the folder containing my app thanks to the following block:
location / {
root /my_app;
}
Of course if you already have another service running on port 80 of the same server you’ll have to think about how you want to organize your services and modify your nginx config accordingly.
Plug to API Backend
Everything is happening inside the src
folder from now on.
Set Dev and Prod Server Names Once For All
My API development server is running on http://127.0.0.1
while my API production is running on http://api.example.com
so in order to avoid changing my config for every deployment I created the following http-constants.js
file at the root of the src
folder:
import axios from 'axios'
let baseURL
if (!process.env.NODE_ENV || process.env.NODE_ENV === 'development') {
baseURL = 'http://127.0.0.1/'
} else {
baseURL = 'http://api.example.com'
}
export const HTTP = axios.create(
{
baseURL: baseURL
})
and then in every .vue
file needing Axios, import HTTP
instead of Axios:
import {HTTP} from '../http-constants'
HTTP.get(...).then(...).catch(...)
Note: this proxying feature might be done more easily in the config/index.js
using the proxyTable
directive but for some reason it did not work for me.
Create The App
Let’s create an app called ShowGreetings that gets an Hello World
message from an API. The API endpoint is /greetings
and returns the following JSON message when sending a GET request:
{message: "Hello World"}
First create the new Vue component called ShowGreetings.vue
in src/components
:
<template>
<div>
<button @click="getGreetings">Get Greetings</button>
<h1 v-if="greetings"></h1>
<p class="error" v-if="errorMessage"></p>
</div>
</template>
<script>
import {HTTP} from '../http-constants'
export default {
name: 'ShowGreetings',
data () {
return {
greetings: '',
errors: ''
}
},
methods: {
getGreetings: function () {
HTTP.get('/greetings')
.then(response => {
this.greetings = response.data.message
})
.catch(e => {
this.errors = e
})
}
}
}
</script>
<style scoped>
.error {
color: red;
}
</style>
This component tries to fetch the API backend when you click on a button and displays the returned message. If an error is returned, it displays the error.
Now update the router in order to take this new component into account. Here is our new index.js
in src/router
:
import Vue from 'vue'
import Router from 'vue-router'
import HelloWorld from '@/components/HelloWorld'
import ShowGreetings from '@/components/ShowGreetings'
Vue.use(Router)
export default new Router({
routes: [
{
path: '/',
name: 'HelloWorld',
component: HelloWorld
},
{
path: '/show-greetings',
name: 'ShowGreetings',
component: ShowGreetings
}
]
})
We created a named route called “ShowGreetings” so we can now refer to the route by its name rather than its path (much more flexible).
Last of all, edit the App.vue
component in src
so a link to our new component appears on the home page:
<template>
<div id="app">
<img src="./assets/logo.png">
<router-link :to="{ name: 'ShowGreetings'}">Show Greetings</router-link>
<router-view/>
</div>
</template>
<script>
export default {
name: 'App'
}
</script>
<style>
#app {
font-family: 'Avenir', Helvetica, Arial, sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
text-align: center;
color: #2c3e50;
margin-top: 60px;
}
</style>
Here we just added a new router-link
tag.
Conclusion
Once you understand how all the layers are interacting, it becomes very easy to build an SPA with Vue.js that can scale from a basic project to a complex project in production.
This whole little project is available on my GitHub if needed.