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 how to add navigation guards to do checks before navigation is done.
Global Before Guards
Router parameter or query changes won’t trigger enter or leave navigation guards.
We can watch the $route
object or use the beforeRouteUpdate
in-component guard to check those changes.
For other route changes, we can use navigation guards to run checks before navigating to a route.
The beforeEach
method takes a callback as follows:
const router = new VueRouter({ ... })
router.beforeEach((to, from, next) => {
// ...
})
The 3 parameters are the following:
to
— the target route object being navigated tofrom
— the current route that’s being navigated fromnext
— a function that’s called to resolve the hook
The next
function takes one argument. It can take on several possible values.
If nothing is passed in, then we can navigate to the next route. If we pass in false
, then the current navigation is aborted.
If we pass in '/'
or { path: '/' }
, then we redirect to the /
route. The current navigation is aborted and a new one is started. We can also set the replace
and name
properties like as we do with router.push
or router.replace
.
We can also pass in an Error
instance since Vue 2.4.0, then the navigation will be aborted and the error will be passed to callbacks registered via router.onError()
.
We should only call next
once in our guard.
For example, we can create a navigation guard to check navigation 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
}
];
const router = new VueRouter({
routes
});
router.beforeEach((to, from, next) => {
if (to.path === "/") {
return next();
} if (!localStorage.getItem("token")) {
next("/");
} else {
next();
}
});
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, our navigation guard first checks if we’re going to /
, if we are, then we let them navigate to it.
Otherwise, we check 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.
Global Resolve Guards
Vue Router also has router.beforeResolve
, which is similar to router.beforeEach
but it’s called right before navigation is confirmed, and after all in-component guards and async route components are resolved.
Global After Hooks
We can also run code after navigation is done with the afterEach
hook.
For example, we can use it as follows:
src/index.js
:
const Foo = { template: "<div>foo</div>" };
const Bar = { template: "<div>bar</div>" };
const routes = [
{
path: "/",
component: Foo
},
{
path: "/bar",
component: Bar
}
];
const router = new VueRouter({
routes
});
router.afterEach((to, from) => {
alert(`Navigated to ${to.path}`);
});
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](https://unpkg.com/vue/dist/vue.js)"></script>
<script src="[https://unpkg.com/vue-router/dist/vue-router.js](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 we get an alert box that shows where we navigate to when we navigate.
Note that it doesn’t have the next
function in the callback signature because it can’t go to the next route.
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.
Conclusion
We run code before and after navigation with navigation guards.
Before hooks are handy for doing checks before navigation is done. For example, it’s handy for authentication checks.
After hooks are handy for running code after navigation is done.
We can define hooks per route or globally.