Vue Router Router and Component Guards and Navigation Resolution Flow
Tags: vue, javascript, webdev, programming
Published: 2021-01-25 23:20:56

**Check out my books on Amazon at https://www.amazon.com/John-Au-Yeung/e/B08FT5NT62**

**Subscribe to my email list now at http://jauyeung.net/subscribe/**

Vue.js is an easy to use web app framework that we can use to develop interactive front end apps.

Vue Router is a URL router that maps URLs to components.

In this article, we’ll look at defining route and in-component guards and look at the full navigation resolution flow.

Per-Route Guard
===============

We can also define navigation guards per route. For example, we can define it as follows:

`src/index.js` :

```
const Login = { template: "<div>login</div>" };  
const Profile = { template: "<div>profile</div>" };  
const routes = [  
  {  
    path: "/",  
    component: Login  
  },  
  {  
    path: "/profile",  
    component: Profile,  
    beforeEnter: (to, from, next) => {  
      if (!localStorage.getItem("token")) {  
        next("/");  
      } else {  
        next();  
      }  
    }  
  }  
];

const router = new VueRouter({  
  routes  
});

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

`index.html` :

```
<!DOCTYPE html>  
<html>  
  <head>  
    <title>App</title>  
    <meta charset="UTF-8" />  
    <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="src/index.js"></script>  
  </body>  
</html>
```

The `beforeEnter` hook for `/profile` checks if `localStorage.token` is defined. If it is, then we let them through. Otherwise, we go back to `/` .

Therefore, if `localStorage.token` is defined, then we can go to `/#/profile` and see `profile` displayed. Otherwise, we get redirected back to `/#/` and see login.

The signature is the same as the global `beforeEach` callback.

In-Component Guards
===================

We can define navigation guards inside a component, the hooks are the following:

*   `beforeRouteEnter`
*   `beforeRouteUpdate`
*   `beforeRouteLeave`

For example, we can use them as follows:

beforeRouteEnter
----------------

If we want to check if a token is present before proceeding to a route, we can write some like the following code:

`src/index.js` :

```
const Login = { template: "<div>login</div>" };  
const Profile = {  
  data() {  
    return { user: "foo" };  
  },  
  template: "<div>profile</div>",  
  beforeRouteEnter(to, from, next) {  
    if (!localStorage.getItem("token")) {  
      return next("/");  
    }  
    next(vm => {  
      alert(\`You're logged in as ${vm.user}`);  
    });  
  }  
};  
const routes = [  
  {  
    path: "/",  
    component: Login  
  },  
  {  
    path: "/profile",  
    component: Profile  
  }  
];

const router = new VueRouter({  
  routes  
});

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

`index.html` :

```
<!DOCTYPE html>  
<html>  
  <head>  
    <title>App</title>  
    <meta charset="UTF-8" />  
    <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="src/index.js"></script>  
  </body>  
</html>
```

In the code above, we have the `beforeRouteEnter` hook to check if the token is present in local storage. If it is then, we proceed. Otherwise, we go back to `/` .

If we proceed, then `next` is called with a callback with `vm` , which is the component object as the parameter. Therefore, we can get the `user` property from `vm` in the `next` callback.

In the end, we should see ‘You’re logged in as foo’ alert box displayed if a `localStorage.token` is defined.

Only `beforeRouteEnter` accepts a callback for `next` since the component isn’t available before navigation is done.

beforeRouteUpdate
-----------------

We can use `beforeRouteUpdate` as follows:

`src/index.js` :

```
const Profile = {  
  data() {  
    return {  
      id: undefined  
    };  
  },  
  template: "<div>id: {{id}}</div>",  
  beforeRouteUpdate(to, from, next) {  
    this.id = to.params.id;  
    next();  
  }  
};  
const routes = [  
  {  
    path: "/profile/:id",  
    component: Profile  
  }  
];

const router = new VueRouter({  
  routes  
});

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

`index.html` :

```
<!DOCTYPE html>  
<html>  
  <head>  
    <title>App</title>  
    <meta charset="UTF-8" />  
    <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="src/index.js"></script>  
  </body>  
</html>
```

Then when we go `/#/profile/1` , then `/#/profile/2` , we’ll see `id: 2` displayed.

`beforeRouteUpdate` watches for route updates, so it does nothing when the route first loads.

beforeRouteLeave
----------------

We can use `beforeRouteLeave` to run code before a user navigates away from a route.

For example, we can use it as follows:

`src/index.js` :

```
const Profile = {  
  template: "<div>profile</div>",  
  beforeRouteLeave(to, from, next) {  
    const answer = window.confirm("Are you sure you want to leave?");  
    if (answer) {  
      next();  
    } else {  
      next(false);  
    }  
  }  
};  
const routes = [  
  {  
    path: "/profile",  
    component: Profile  
  }  
];

const router = new VueRouter({  
  routes  
});

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

`index.html:`

```
<!DOCTYPE html>  
<html>  
  <head>  
    <title>App</title>  
    <meta charset="UTF-8" />  
    <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="src/index.js"></script>  
  </body>  
</html>
```

In the code above, we have the `beforeRouteLeave` hook that runs before a user tries to leave a route.

We have:

```
const answer = window.confirm("Are you sure you want to leave?");
```

to ask the user if he wants to leave. If he clicks OK, then the navigation will continue by calling `next()` . Otherwise, `next(false)` is called and navigation is canceled.

Full Navigation Resolution Flow
===============================

The full flow for navigation resolution is as follows:

1.  navigation triggered
2.  `beforeRouteLeave` called
3.  global `beforeEach` guard called
4.  `beforeRouterUpdate` in component called
5.  `beforeEnter` in route configs called
6.  resolve async route components
7.  `beforeEnter` in activated components called
8.  global `beforeResolve` guards called
9.  navigation confirmed
10.  global `afterEach` hooks called
11.  DOM updates triggered
12.  callbacks passed to `next` in `beforeRouteEnter` called

Conclusion
==========

We can have route guards in individual routes and components. They have their own hooks but they can serve the same purpose per route or component.

The only exception is the `beforeRouteEnter` which takes a callback in the `next` function.

Global navigation guards are generally called before local ones.