Vue-Router: Navigating Vue.js Apps

Introduction

Vue-Router is a JavaScript package which allows you to set up routing for Single Page Applications (SPA).

SPA refers to a web application which only serves a single index.html page and renders content dynamically, being this the way modern JavaScript frameworks such as React.js or Vue.js are most likely setup.

Why use Vue-Router?

Using a SPA has a lot of advantages, but one of the main caveats is that all the components of the web page are being delivered, added or removed via JavaScript with no extra HTML pages being fetched from the server. This is what SPA's are all about, but the main issue is being able to navigate through "pages" as users are accustomed to with most websites.

This is where Vue-Router kicks in!

Installation and Setup

If you are using Vue CLI, you might have already encountered a situation where you're asked if you want to include Vue-Router to the project configuration.

Vue-Router can easily be installed using your preferred package manager, be it NPM or Yarn:

$ npm install vue-router
OR  
$ yarn add vue-router

This will add vue-router to your package.json file and you are good to go.

It's recommended to write router related code inside a separate file called router.js and add it to the Vue application inside the main.js file:

import Vue from 'vue'  
import App from './App'  
import Router from './router // Router being imported

Vue.config.productionTip = false

/* eslint-disable no-new */
new Vue({  
  el: '#app',
  router: Router, // Router added to the Vue instance
  components: { App },
  template: '<App/>'
})

The router.js File: Creating Routes

Firstly we need to import router from the Vue-Router package, and add it to Vue as a plugin with the method use.

After that, we treat the router as an object, which will hold all the routes. And of course, we have to export it so the rest of the application can access it.

import Vue from 'vue'  
import Router from 'vue-router'

Vue.use(Router)

export default new Router({  
  routes: []
})

The routes property is an array which will hold objects, each of which will be the specific routes for your web page. The most basic one would look like this:

 const Home = { template: '<div>Welcome to Stack Abuse!</div>' }
 routes: [
   {
     path: '/',
     name: 'Home',
     component: Home
   }
 ]

Vue-Router accepts plentiful properties for it is routed objects:

  • path: This is the relative path to the base route of your web application where you want a certain component being rendered on.

  • name: The name property will be useful when referring to this particular object inside your Vue components.

  • component: With this property, a Vue component will be injected into the route path you assign.

  • redirect: A component route can be redirected to another route when it gets matched.

  • alias: Pretty similar to redirect, but whenever you enter the alias path, the router will show the router root path.

const router = new VueRouter({  
  routes: [
    { 
      path: '/users', 
      component: Users, 
      alias: '/alias' // When entering '/alias' the content of the component Users will render
    }
  ]
})
  • children: This is an array with more routes which will be concatenated to the path whilst keeping the parent path in the URL.
const router = new VueRouter({  
  routes: [
    { 
      path: '/users/', 
      component: Users,
      children: [
        {
          // when /users/profile gets matched
          path: 'profile',
          component: UserProfile
        },
        {
          // when /users/info gets matched
          path: 'info',
          component: UserInfo
        }
      ]
    }
  ]
})
  • params: These are properties or certain information that could be passed to the component.

  • meta: Whenever a route (or multiple routes) are matched to the path, the meta property will be available. This is especially useful when using navigation guards.

We can also add a mode property to the Router object. By default, Vue-Router is set up to hash mode, which means all URLs will have a # at the end. This can be removed, as you most likely will want, by setting the mode property to history:

export default new Router({  
  mode: 'history',
  routes: []
})

This mode comes with a problem though. Without a proper configuration, accessing, for an example, http://localhost:8080/user/id, directly in their browser will result in a 404 error.

To fix this, you need to add a simple fallback route to your server. If the path does not match any static assets, it should serve the same index.html.

For example, if you would want to setup history mode inside a Node.js server this could be set up:

const http = require('http')  
const fs = require('fs')  
const httpPort = 80

http.createServer((req, res) => {  
  fs.readFile('index.htm', 'utf-8', (err, content) => {
    if (err) {
      console.log('We cannot open "index.htm" file.')
    }

    res.writeHead(200, {
      'Content-Type': 'text/html; charset=utf-8'
    })

    res.end(content)
  })
}).listen(httpPort, () => {
  console.log('Server listening on: http://localhost:%s', httpPort)
})

Accessing the Router from a Component

Given a particular component, you can access the router object with the $ notation in order to react to user input or being able to render routes depending on a certain condition.

To have the Vue-Router routes be rendered you will need to pass the <router-view> tag inside a Vue component.

You could also access the routes from an <a> tag, but this will trigger a page re-render, to avoid this behavior you could use router-link with the to property instead of a href. This special tag tells the router to navigate to the given "page".

<template>  
  <div>
     <h3> Vue Router - Stack Abuse </h3>
     <h4> { $router.params.id } </h4>
     <button @click="goBack"> Go Back </button>
     <router-view></router-view>
     <router-link :to="/users"> Users Route </router-link>
  </div>
</template>

export default {  
  methods: {
    goBack () {
       this.$router.go(-1)
    }
  }
}

The router object has certain methods attached to it, which will help you redirect your application to the desired URL:

  • $router.go(): Takes a number as a parameter which refers to the number of positions you want to go forward or behind inside the browser history stack array.

  • $router.push(): This function can take a string, referring to the name or path of the route you want to access, or an object with the route configuration.

router.push(  
  { 
    name: 'users'
    params: { id: 1 }
  }
)
  • $router.replace(): Pretty much the same as push, instead of that other than adding a new entry to the browser history stack, it replaces the current one with the one we are adding.

Route Guards

Most applications need a way to forbid access to certain routes. This can be easily done with route guards, which we discussed previously when talking about the meta property.

Each route has a navigation resolution flow, and there are certain hooks that can be used for your benefit.

  • beforeEach: Certainly the most useful one when needing a global guard to keep users out of restricted areas, you can set up a callback function which will receive to, from, next from the beforeEach function which lets you know, where you are navigation from, and to, as well as the next step to take:
router.beforeEach((to, from, next) => {  
  if (to.matched.some(record => record.meta.auth)) {
    if (!userIsLoggedIn)
      next('/')
      console.log('Not allowed here')
    } else {
      next('/secret-endpoint')
    }
  }
})
  • beforeEnter: Gets triggered before a user enters a certain route.

  • afterEach: This gets called whenever navigation has been successful for each route.

Conclusion

Vue-Router is a must-have tool if you build any Vue.js Single Page Applications, it gives you the flexibility to manage the user's navigation to the fullest.

You can always get more information visiting the Vue-Router official page.

Author image
About Lucas Otero
Buenos Aires, Argentina Github