Single page apps are possible on the web due to the HTML5 history API: History – Web APIs | MDN What the history API does is allow the developer to adjust the history of the web browser without changing pages and can still have permanent URLs for deep linking. Why would developers want this? It allows developers a significant load time change and more control over transitions within their site. Each route only loads the data unique to the route after the initial load. So in a traditional application, if you have a CSS file, a JS file and some images. Those are loaded every time you make a page request. In a single page application, these are loaded initially on the first load and any other images, data, CSS is loaded through AJAX. In the end this saves lots of server requests and makes for a much speedier app. There are some downfalls when building apps like this discussed here: The disadvantages of single page applications by Adam Silver. I agree with some of the statements, but when building smaller apps like Roast, the code is generally more maintainable and super easy to transfer to a hybrid mobile app which is our end goal. Single Page Applications do a good job of separating the visual display from the data source which is perfect for transferring to mobile.

Now learning the HTML 5 History API can be challenging, but thanks to Vue Router Introduction · vue-router, all of the hard stuff is taken care of such as pushing and popping states. And since we are building our app in VueJS, it works seamlessly with everything else in VueJS.

In this tutorial, we are simply going to set up our home route, a cafes list route, an individual cafe route, and a cafe submission route. These will be on our front end only routes and the next tutorial we will add the backend component for the API.

Step 1: Configure Routes File

We will need to open our /resources/assets/js/routes.js file. This will be where we maintain every route in our application. We will need to import Vue and VueRouter first into the file so we can tell Vue that we are using VueRouter to handle the routes in our application. The top of your routes.js file should look like:

/*
|-------------------------------------------------------------------------------
| routes.js
|-------------------------------------------------------------------------------
| Contains all of the routes for the application
*/

/*
    Imports Vue and VueRouter to extend with the routes.
*/
import Vue from 'vue'
import VueRouter from 'vue-router'

/*
    Extends Vue to use Vue Router
*/
Vue.use( VueRouter )

First we import Vue from the ‘vue’ package and then import VueRouter from the ’vue-router’ package. Then we instructed Vue to use VueRouter. Pretty simple right off of the bat.

Step 2: Add Routes

We will need to add 4 routes to our app right off of the bat. The routes will be:

  • / -> Home
  • /cafes -> Cafe Listing
  • /cafes/new -> Add Cafe
  • /cafes/:id -> Display Individual Cafe

The Vue Router documentation is very thorough on how to add routes: Getting Started · vue-router. One thing I like to do is use named routes. What this means is I can reference routes by a specific name instead of typing out the entire path when I write links.

Each route is an object in a routes array that we use when we construct a new VueRouter. Since we have the routes in a separate file, we export the default module so we can import it into our app.js file.

The following is what we should have in our /resources/assets/js/routes.js file:

/*
|-------------------------------------------------------------------------------
| routes.js
|-------------------------------------------------------------------------------
| Contains all of the routes for the application
*/

/*
    Imports Vue and VueRouter to extend with the routes.
*/
import Vue from 'vue'
import VueRouter from 'vue-router'

/*
    Extends Vue to use Vue Router
*/
Vue.use( VueRouter )

/*
    Makes a new VueRouter that we will use to run all of the routes
    for the app.
*/
export default new VueRouter({
    routes: [
        {
            path: '/',
            name: 'home',
            component: Vue.component( 'Home', require( './pages/Home.vue' ) )
        },
        {
            path: '/cafes',
            name: 'cafes',
            component: Vue.component( 'Cafes', require( './pages/Cafes.vue' ) )
        },
    {
      path: '/cafes/new',
      name: 'newcafe',
      component: Vue.component( 'NewCafe', require( './pages/NewCafe.vue' ) )
    },
    {
      path: '/cafes/:id',
      name: 'cafe',
      component: Vue.component( 'Cafe', require( './pages/Cafe.vue' ) )
    }
    ]
});

There’s a few things to point out. Each route has a name. For example, the home route has the name of ‘home’ which we can reference from any place in our app. We also have a key in each route object called ‘component’. This defines the Vue component used to render each page. Like I mentioned before, I store these in a special directory: /resources/assets/js/pages. The other thing to point out, is that the last route (/cafes/:id) notice the ‘:id’ at the end. This is a dynamic route segment (Dynamic Route Matching · vue-router) which allows us to define a pattern matched ID to dynamically load a specific cafe.

Step 3: Add Page Components

In Vue Router, all of the pages being rendered are Components. In the last tutorial we created a directory to house these pages /resources/assets/js/pages. We need to add the following files:

  • /resources/assets/js/pages/Cafe.vue
  • /resources/assets/js/pages/Cafes.vue
  • /resources/assets/js/pages/Home.vue
  • /resources/assets/js/pages/NewCafe.vue

Each file should contain just the shell of a Vue single file component which looks like:

<style>

</style>

<template>
  <div>

  </div>
</template>

<script>
  export default {

  }
</script>

If you haven’t used Single File Vue Components before, I’d read about them here: Single File Components — Vue.js. Simply put they are your JS, HTML, and CSS all scoped within a single file. Since we are using Laravel and Laravel Mix, these compile to the appropriate files out of the box. I find this approach amazing for all apps, single page or not, because it really helps maintain a clean codebase for all components. You can even scope your CSS so it only applies to the component it’s registered in.

Step 4: Import Router into App.js

We now have our routes.js file ready to rock and roll and basic page template components outlined. We are now ready to import our VueRouter into our /resources/assets/js/app.js file.

To do that, all we have to do is add

import router from './routes.js'

right after

import Vue from 'vue';

Next we will instruct our Vue instance to use the router we just imported by adding the router to the new Vue instruction like this:

new Vue({
    router
}).$mount('#app')

Our app.js file should now look like:

window._ = require('lodash');

try {
    window.$ = window.jQuery = require('jquery');

    require('foundation-sites');
} catch (e) {}

/**
 * We'll load the axios HTTP library which allows us to easily issue requests
 * to our Laravel back-end. This library automatically handles sending the
 * CSRF token as a header based on the value of the "XSRF" token cookie.
 */

window.axios = require('axios');

window.axios.defaults.headers.common['X-Requested-With'] = 'XMLHttpRequest';

/**
 * Next we will register the CSRF Token as a common header with Axios so that
 * all outgoing HTTP requests automatically have it attached. This is just
 * a simple convenience so we don't have to attach every token manually.
 */

let token = document.head.querySelector('meta[name="csrf-token"]');

if (token) {
    window.axios.defaults.headers.common['X-CSRF-TOKEN'] = token.content;
} else {
    console.error('CSRF token not found: https://laravel.com/docs/csrf#csrf-x-csrf-token');
}

import Vue            from 'vue';
import router           from './routes.js'


new Vue({
    router
}).$mount('#app')

We now define Vue, configure axios and configure the Vue Router so our app has a few pages. Our app still doesn’t do too much yet, but we are getting there!

Step 5: Build

This is just a reminder step that if you haven’t been running npm run watch then you should run an npm run dev to build your app on your development environment.

If you are curious, you can log in socially and visit one of the routes defined. The next tutorial will go through adding the API routes and using Axios for HTTP requests to our API. If you want to take a look at the features of Axios check out their documentation: GitHub – axios/axios: Promise based HTTP client for the browser and node.js. It’s super easy to use and we already have it configured out of the box with Laravel to attach the token so we can access our API. The next tutorials will cover building these API requests in Javascript, building the API endpoints on the Laravel side, then configuring Vuex to store the data we get returned. Once we have that done we can start making the app look pretty and doing some other cool stuff to the app! Let me know if there are any questions, suggestions, or opinions on the structure so far! Remember, you can follow along on GitHub: https://github.com/serversideup/roastandbrew