Vue Routing: Navigation Guards
Navigation guards are functions that are executed before or after a route is navigated to. They provide a powerful way to control access to routes, perform actions before a route changes, or even redirect users. They're a core part of building secure and dynamic Vue applications with Vue Router.
Types of Navigation Guards
There are three main types of navigation guards:
- Global Guards: Applied to all routes in your application.
- Route Guards: Applied to specific routes.
- Component Guards: Applied to a specific component rendered by a route.
1. Global Guards
Global guards are defined using router.beforeEach, router.beforeResolve, and router.afterEach on the router instance.
router.beforeEach( (to, from, next) => { ... }): This guard is executed before the route is navigated to. It's the most commonly used guard.to: The target Route object being navigated to.from: The current Route object being navigated away from.next: A function that must be called to resolve the guard. It accepts three arguments:next(): Proceed to the next route.next(false): Abort the current navigation.next('/path'): Redirect to a different route.next({ path: '/path', replace: true }): Redirect and replace the current history entry.
router.beforeResolve( (to, from, next) => { ... }): Similar tobeforeEach, but called after all component instances have been created and before the route is fully resolved. Useful for asynchronous operations that need to happen after components are mounted.router.afterEach( (to, from) => { ... }): This guard is executed after the route has been navigated to and the component has been mounted. It doesn't receive thenextfunction because the navigation has already completed. Useful for analytics, page title updates, or other post-navigation tasks.
Example (Global Guard - Authentication):
// router/index.js
import Vue from 'vue'
import VueRouter from 'vue-router'
import Home from '../views/Home.vue'
import Login from '../views/Login.vue'
Vue.use(VueRouter)
const routes = [
{
path: '/',
name: 'home',
component: Home
},
{
path: '/login',
name: 'login',
component: Login
}
]
const router = new VueRouter({
routes
})
router.beforeEach((to, from, next) => {
const isLoggedIn = localStorage.getItem('isLoggedIn'); // Example authentication check
if (to.name === 'login') {
if (isLoggedIn) {
next('/'); // Redirect to home if already logged in
} else {
next(); // Proceed to login page
}
} else {
if (!isLoggedIn) {
next('/login'); // Redirect to login if not logged in
} else {
next(); // Proceed to the route
}
}
})
export default router
2. Route Guards
Route guards are defined directly within the route configuration object using the beforeEnter property.
beforeEnter( (to, from, next) => { ... }): Functions the same asrouter.beforeEach, but applies only to the specific route.
Example (Route Guard - Role-Based Access):
// router/index.js
import Vue from 'vue'
import VueRouter from 'vue-router'
import Home from '../views/Home.vue'
import Admin from '../views/Admin.vue'
Vue.use(VueRouter)
const routes = [
{
path: '/',
name: 'home',
component: Home
},
{
path: '/admin',
name: 'admin',
component: Admin,
beforeEnter: (to, from, next) => {
const userRole = localStorage.getItem('userRole'); // Example role check
if (userRole === 'admin') {
next(); // Proceed to admin page
} else {
next('/'); // Redirect to home if not an admin
}
}
}
]
const router = new VueRouter({
routes
})
export default router
3. Component Guards
Component guards are defined within the component itself using beforeRouteEnter, beforeRouteUpdate, and beforeRouteLeave.
beforeRouteEnter(to, from, next) { ... }: Called before the component is created, allowing you to access the route being navigated to. You cannot access the component instance (this) within this guard. Usenext(vm => { ... })to access the component instance after it's created.to: The target Route object being navigated to.from: The current Route object being navigated away from.next: A function that must be called to resolve the guard.
beforeRouteUpdate(to, from, next) { ... }: Called when the route associated with the component changes, but the component instance is reused. Useful for updating data based on route parameters. You can access the component instance (this) within this guard.beforeRouteLeave(from, next) { ... }: Called when the component is about to be navigated away from. Useful for confirming changes or saving data. You can access the component instance (this) within this guard.
Example (Component Guard - Confirmation Before Leaving):
<template>
<div>
<h1>My Component</h1>
<p>Unsaved changes will be lost!</p>
</div>
</template>
<script>
export default {
data() {
return {
isDirty: true // Example: Component has unsaved changes
};
},
beforeRouteLeave(to, from, next) {
if (this.isDirty) {
if (confirm('Are you sure you want to leave? Unsaved changes will be lost.')) {
next();
} else {
next(false); // Cancel navigation
}
} else {
next(); // Proceed to the next route
}
}
};
</script>
Guard Execution Order
When multiple guards are involved, they are executed in the following order:
- Global
beforeEachguards - Route-specific
beforeEnterguards - Component
beforeRouteEnterguards (for the component being navigated to) - Component
beforeRouteUpdateguards (if the component is reused) - Global
beforeResolveguards - Component
beforeRouteLeaveguards (for the component being navigated away from) - Global
afterEachguards
Important Considerations
next()is crucial: Always callnext()(ornext(false)ornext('/path')) within each guard to resolve the navigation. Failing to do so will cause the navigation to hang.- Asynchronous Operations: Use
async/awaitor Promises within guards to handle asynchronous operations (e.g., fetching data from an API). Make sure to callnext()after the asynchronous operation completes. - Guard Complexity: Keep guards concise and focused on specific tasks. Complex logic should be moved to separate functions or services.
- Avoid Side Effects in
afterEach:afterEachshould primarily be used for logging or analytics, as it's executed after the navigation has completed and cannot affect the outcome.
This provides a comprehensive overview of Vue Router navigation guards. Remember to choose the appropriate guard type based on your specific needs and to always call next() to resolve the navigation.