Web Development

Introduction to Vue Router

February 11th, 2020 | By John Au-Yeung | 11 min read

In this article, discover how to use the Vue Router in our Vue.js app.

To create a single-page app with Vue.js, we must add a router library to route URLs to our components. Vue.js has a Vue Router routing library to handle this routing.

Getting Started with Vue Router

We can start by including Vue and Vue Router scripts on our app's page. Then, we have to add a div to house our Vue app. Also, we have to include the router-view component to view the route's content.

To add links, we add router-link components with the path we want to go to. To do all that, we write the following:

index.html:

<!DOCTYPE html>
<html lang="en">
  <head>
    <title>App</title>
    <script src="https://unpkg.com/vue/dist/vue.js"></script>
    <script src="https://unpkg.com/vue-router/dist/vue-router.js"></script>
  </head>
  <body>
    <div id="app">
      <router-link to="/foo">Foo</router-link>
      <router-link to="/bar">Bar</router-link>
      <div>
        <router-view></router-view>
      </div>
    </div>
    <script src="./index.js"></script>
  </body>
</html>


In the code above, we find the script tags for Vue and Vue Router in the head tag. Then, we have the div with the ID app to house the Vue app.

The router-view shows our content.

In the div, we have the router-link components with the to prop to pass in the path we want the links to go to.

Router-Link is a component that comes with the Vue Router.

Finally, we have our index.js script file. In there, we'll add our code for the route components and do the routing.

In index.js, we have:

const Foo = { template: "<div>foo</div>" };
const Bar = { template: "<div>bar</div>" };
const routes = [
  { path: "/foo", component: Foo },
  { path: "/bar", component: Bar }
];

const router = new VueRouter({
  routes
});

const app = new Vue({
  router,
  el: "#app"
});


In the code above, we have the Foo and Bar routes to show Foo and Bar, respectively.

Then, we use the routes array to map the components to the paths to which we want to map the routes.

Next, we created a new instance of VueRouter with an object that had our routes in it.

Then, we created a new Vue instance with the router object and an el property with the #app div we added to house our app.

Once we do all that, we should have links at the top of the page that show Foo and Bar links, respectively. When we click them, we'll see Foo and Bar, respectively.

Dynamic Route Matching

To map routes to a dynamic URL parameter, we can add a placeholder for it by creating a name and adding a colon before it.

Then, in our Vue instance, we can watch the $route object for changes in the parameter and run code accordingly.

For instance, we can write the following code to use route parameters in our routes:

index.js:

const User = {
  template: "<div>User {{id}}</div>",
  data() {
    return {
      id: undefined
    };
  },
  watch: {
    $route(to, from) {
      this.id = to.params.id;
    }
  }
};

const routes = [
    { path: "/user/:id", component: User }
];

const router = new VueRouter({
  routes
});

const app = new Vue({
  router,
  el: "#app"
});


index.html:

<!DOCTYPE html>
<html lang="en">
  <head>
    <title>App</title>
    <script src="https://unpkg.com/vue/dist/vue.js"></script>
    <script src="https://unpkg.com/vue-router/dist/vue-router.js"></script>
  </head>
  <body>
    <div id="app">
      <router-link to="/user/1">User 1</router-link>
      <router-link to="/user/2">User 2</router-link>
      <router-view></router-view>
    </div>
    <script src="./index.js"></script>
  </body>
</html>


In the code above, we have:

watch: {
    $route(to, from) {
      this.id = to.params.id;
    }
 }


to watch for URL parameter changes. We get the :id route parameter by using to.params.id.

Then, we set that to this.id so that we can use it in our template, which is:

<div>User {{id}}</div>


To define the routes, we have:

const routes = [
    { path: "/user/:id", component: User }
];


The :id part of the string is the URL parameter placeholder.

In index.html, we have two router-link components:

<router-link to="/user/1">User 1</router-link>
<router-link to="/user/2">User 2</router-link>


When we click them, we should see the ID in our template update as the :id route parameter is changing.

Catch-all/404 Not Found Route

We can use the * sign as a wildcard character.

To use it, we can write:

index.html

<!DOCTYPE html>
<html lang="en">
  <head>
    <title>App</title>
    <script src="https://unpkg.com/vue/dist/vue.js"></script>
    <script src="https://unpkg.com/vue-router/dist/vue-router.js"></script>
  </head>
  <body>
    <div id="app">
      <router-view></router-view>
    </div>
    <script src="./index.js"></script>
  </body>
</html>


index.js

const NotFound = {
  template: "<div>not found</div>"
};

const routes = [{ path: "*", component: NotFound }];

const router = new VueRouter({
  routes
});

const app = new Vue({
  router,
  el: "#app"
});


In the code above, we have the NotFound component, which is the component for our catch-all route since we have:

{ path: "*", component: NotFound }


in the routes array.

Therefore, when we go to any URL, we'll see 'not found' displayed.

Nested Routes

We can nest routes by adding a children's property to our route entries with our child routes.

For example, we can write the following to create our nested routes:

index.js:

const User = {
  template: `<div>
    User {{id}}
    <router-view></router-view>
  </div>`,
  data() {
    return {
      id: undefined
    };
  },
  watch: {
    $route(to, from) {
      this.id = to.params.id;
    }
  }
};

const Profile = {
  template: `<div>Profile</div>`
};

const routes = [
  {
    path: "/user/:id",
    component: User,
    children: [
      {
        path: "profile",
        component: Profile
      }
    ]
  }
];

const router = new VueRouter({
  routes
});

const app = new Vue({
  router,
  el: "#app"
});


index.html:

<!DOCTYPE html>
<html lang="en">
  <head>
    <title>App</title>
    <script src="https://unpkg.com/vue/dist/vue.js"></script>
    <script src="https://unpkg.com/vue-router/dist/vue-router.js"></script>
  </head>
  <body>
    <div id="app">
      <router-link to="/user/1">User</router-link>
      <router-link to="/user/1/profile">Profile</router-link>
      <router-view></router-view>
    </div>
    <script src="./index.js"></script>
  </body>
</html>


The difference between the nested route example above and the earlier examples is that we have the following route definition:

const routes = [
  {
    path: "/user/:id",
    component: User,
    children: [
      {
        path: "profile",
        component: Profile
      }
    ]
  }
];


The children's property is used to nest our child routes.

In the template for the User component, we have the router-view added as follows to display items in child routes:

<div>
   User {{id}}
   <router-view></router-view>
</div>


We also have the following router-link components:

<router-link to="/user/1">User</router-link>
<router-link to="/user/1/profile">Profile</router-link>


Multiple Router Views

To have multiple router-view components in the same app, we have to name them.

We can define our routes as follows and put them in their own router-view:

index.js:

const Foo = {
  template: `<div>foo</div>`
};

const Bar = {
  template: `<div>bar</div>`
};

const Baz = {
  template: `<div>baz</div>`
};

const router = new VueRouter({
  routes: [
    {
      path: "/",
      components: {
        default: Foo,
        a: Bar,
        b: Baz
      }
    }
  ]
});

const app = new Vue({
  router,
  el: "#app"
});


index.html:

<!DOCTYPE html>
<html lang="en">
  <head>
    <title>App</title>
    <script src="https://unpkg.com/vue/dist/vue.js"></script>
    <script src="https://unpkg.com/vue-router/dist/vue-router.js"></script>
  </head>
  <body>
    <div id="app">
      <router-view></router-view>
      <router-view name="a"></router-view>
      <router-view name="b"></router-view>
    </div>
    <script src="./index.js"></script>
  </body>
</html>

In the code above, we have the router-view named a and the router-view named b defined as in index.html:

<router-view></router-view>
<router-view name="a"></router-view>
<router-view name="b"></router-view>


Then, in our route definition in index.js, we have:

components: {
    default: Foo,
    a: Bar,
    b: Baz
}


To map the router-view with no name to the Foo component, the router-view to the Bar component, and the b router-view to the Baz component.

Then we should see:

foo
bar
baz


displayed on the screen.

Navigation Guards

From the official Vue Router documents:

Navigation guards provided by vue-router are primarily used to guard navigations, either by redirecting them or canceling them.

As such, we can add navigation guards to our routes to watch for route changes and do something before it's complete.

Enter/leave navigation guards won't trigger during params or query changes.

Global Navigation Guards

We can define a global before guard by attaching a route change listener to our router.

To add a global navigation guard, we can write the following:

index.js:

const Foo = {
  template: `<div>foo</div>`
};

const Bar = {
  template: `<div>bar</div>`
};

const Login = {
  template: `<div>login</div>`
};

const router = new VueRouter({
  routes: [
    {
      path: "/foo",
      component: Foo
    },
    {
      path: "/bar",
      component: Bar
    },
    {
      path: "/login",
      component: Login
    }
  ]
});

router.beforeEach((to, from, next) => {
  if (!localStorage.getItem("authToken") && to.path !== "/login") {
    return next("/login");
  }
  next();
});

const app = new Vue({
  router,
  el: "#app"
});


index.html:

<!DOCTYPE html>
<html lang="en">
  <head>
    <title>App</title>
    <script src="https://unpkg.com/vue/dist/vue.js"></script>
    <script src="https://unpkg.com/vue-router/dist/vue-router.js"></script>
  </head>
  <body>
    <div id="app">
      <router-link to="/foo">Foo</router-link>
      <router-link to="/bar">Bar</router-link>
      <router-view></router-view>
    </div>
    <script src="./index.js"></script>
  </body>
</html>


In the code above, we have:

router.beforeEach((to, from, next) => {
  if (!localStorage.getItem("authToken") && to.path !== "/login") {
    return next("/login");
  }
  next();
});


which is our global navigation guard that runs before navigation begins, as indicated by the beforeEach call.

We check that the path that we're going to isn't /login with:

to.path !== "/login"


Then, if localStorage.getItem("authToken") is false, we go to the /login route by calling next('./login). Otherwise, we proceed by calling next().

Per-Route Guard

In a similar way, we can define per-route guards as follows:

const router = new VueRouter({
  routes: [
    {
      path: '/foo',
      component: Foo,
      beforeEnter: (to, from, next) => {
        // ...
      }
    }
  ]
})


These are called before navigation is done and only run when we try to go to the /foo route.

In-Component Guards

We can define in-components in our component as follows:

const Foo = {
  template: `...`,
  beforeRouteEnter (to, from, next) {
    //...
  },
  beforeRouteUpdate (to, from, next) {
    //...
  },
  beforeRouteLeave (to, from, next) {
    //...
  }
}


We have three guards. They are:

  1. beforeRouteEnter: It's called before the route renders the component

  2. beforeRouteUpdate: is called when a route that renders the component has changed, including parameter changes.

  3. *beforeRouteLeave: is called when the rendered route is about to be navigated away from.

Transitions

We can add transition components like any other component to add transitions to router-view.

To add transitions to a Vue app, we can use the transition component and some simple CSS:

styles.css:

.fade-enter-active,
.fade-leave-active {
  transition: opacity 0.9s;
}
.fade-enter,
.fade-leave-to {
  opacity: 0;
}


index.js:

const Foo = {
  template: `<div>foo</div>`
};

const Bar = {
  template: `<div>bar</div>`
};

const router = new VueRouter({
  routes: [
    {
      path: "/foo",
      component: Foo
    },
    {
      path: "/bar",
      component: Bar
    }
  ]
});

const app = new Vue({
  router,
  el: "#app"
});


index.html:

<!DOCTYPE html>
<html lang="en">
  <head>
    <title>App</title>
    <script src="https://unpkg.com/vue/dist/vue.js"></script>
    <script src="https://unpkg.com/vue-router/dist/vue-router.js"></script>
    <link rel="stylesheet" href="./styles.css" />
  </head>
  <body>
    <div id="app">
      <router-link to="/foo">Foo</router-link>
      <router-link to="/bar">Bar</router-link>
      <transition name="fade">
        <router-view></router-view>
      </transition>
    </div>
    <script src="./index.js"></script>
  </body>
</html>


In the code above, we added the styles in styles.css to create our CSS route transition effects. We just changed the opacity for a short moment to add the fade effect.

Then, we added the transition component with the name attribute set to fade so that we could use the classes with the fade-prefix in styles.css to style the transition effects.

In the end, when we click on the router-link, we'll see the fade effect.

Conclusion

We can use Vue Router to map URL paths to components.

To get route parameters, we watch the $route object in our components.

We can also add nested routes by adding a child property with nested routes. Also, we can add a components property to our route and name our router-view to add multiple router-view.

To intercept navigation and do something, we can add navigation guards for various stages of navigation.

Finally, we can use the transition component with some CSS to create route transition effects.

Before deploying your commercial or enterprise Vue apps to production, make sure you are protecting their code against reverse-engineering, abuse, and tampering.

Jscrambler

The leader in client-side Web security. With Jscrambler, JavaScript applications become self-defensive and capable of detecting and blocking client-side attacks like Magecart.

View All Articles

Must read next

Web Development

Advanced Vue Features: Directives, Filters, and Mixins

Some more advanced Vue features can help you solve more specific code needs. In this tutorial, we explore directives, filters, and mixins.

April 13, 2020 | By John Au-Yeung | 9 min read

Introduction to Vue

Vue is one of the top 3 JavaScript front-end frameworks, with many devs giving it a try. See this quick but solid overview on Vue basics to get started.

January 22, 2020 | By John Au-Yeung | 7 min read

Section Divider