路由守卫对于控制 Vue Router 应用程序中的导航至关重要。它们允许您拦截和管理路由转换,确保用户只能根据特定条件(例如身份验证状态或权限)访问应用程序的某些部分。本课将探索 Vue Router 中可用的不同类型的路由守卫:全局、每个路由和组件内。
Route guards 是在导航到路由时执行的函数。它们可用于执行各种任务,例如:
Vue Router 提供了三种主要类型的路由守卫:
每种类型的守卫都提供不同级别的粒度和对路由进程的控制。
全局防护非常适合需要在每次路由更改时执行的任务,例如日志记录、分析或全局身份验证检查。Vue Router 提供了三种类型的全局守卫:beforeEach
、beforeResolve
和 afterEach
。
beforeEach
beforeEach
守卫在导航到每个路由之前执行。它接收三个参数:
to
:要导航到的目标路由对象。from
:当前要导航离开的路线。next
:必须调用才能解决 hook 的函数。该作取决于提供给 next
的参数:
next()
:继续处理链中的下一个钩子。如果没有留下钩子,则确认导航。next(false):
中止当前导航。如果浏览器 URL 已更改(由用户手动或通过编程导航),则它将重置为 from
路由的 URL。next(path)
或 next({ path: '...', query: '...', hash: '...' })
: 重定向到其他路由。下面是一个在允许用户访问某些路由之前使用 beforeEach
检查用户是否经过身份验证的示例:
import { createRouter, createWebHistory } from 'vue-router';
const routes = [
{ path: '/', component: () => import('../components/Home.vue') },
{ path: '/profile', component: () => import('../components/Profile.vue'), meta: { requiresAuth: true } },
{ path: '/login', component: () => import('../components/Login.vue') }
];
const router = createRouter({
history: createWebHistory(),
routes
});
router.beforeEach((to, from, next) => {
const isAuthenticated = localStorage.getItem('token'); // Replace with your actual authentication check
if (to.meta.requiresAuth && !isAuthenticated) {
// Redirect to login page if authentication is required and user is not authenticated
next('/login');
} else {
// Proceed to the route
next();
}
});
export default router;
在此示例中:
meta.requiresAuth
属性设置为 true
来定义需要身份验证的路由 /profile
。beforeEach
守卫中,我们检查目标路由 (to
) 是否需要身份验证,以及用户是否经过身份验证(使用 localStorage.getItem('token')
作为占位符)。next('/login')
将他们重定向到 /login
路由。next()
来允许导航继续。beforeResolve
beforeResolve
守卫类似于 beforeEach
,但它在所有 beforeEach
守卫被解决之后执行,并且在确认导航之前执行。它还接收相同的三个参数:to
、from
和 next
。
此 guard 可用于执行依赖于其他 guard 的结果的任务,或在挂载组件之前解析数据。
router.beforeResolve((to, from, next) => {
// Example: Fetching user data before resolving the route
if (to.meta.requiresAuth) {
fetch('/api/user')
.then(response => response.json())
.then(user => {
if (user) {
// User data fetched successfully, proceed to the route
next();
} else {
// User data not found, redirect to login
next('/login');
}
})
.catch(error => {
console.error('Error fetching user data:', error);
next('/login'); // Redirect to login on error
});
} else {
next();
}
});
在此示例中,我们在解析路由之前从 API 终端节点获取用户数据。如果成功获取了用户数据,我们将继续进行路由。否则,我们会将用户重定向到登录页面。
afterEach
afterEach
守卫在确认导航后执行。它接收两个参数:
to
:要导航到的目标路由对象。from
:当前要导航离开的路线。与 beforeEach
和 beforeResolve
不同,afterEach
守卫不会收到 next
函数,因为导航已经完成。它通常用于记录路由更改或更新分析等任务。
router.afterEach((to, from) => {
// Example: Logging route changes to the console
console.log(`Navigated from ${from.path} to ${to.path}`);
// Example: Sending page view event to Google Analytics
if (typeof window !== 'undefined' && window.gtag) {
window.gtag('config', 'GA_TRACKING_ID', {
'page_path': to.path
});
}
});
在此示例中,我们将路由更改记录到控制台,并将页面查看事件发送到 Google Analytics。
Per-route 守卫允许您定义特定于单个路由的守卫。这对于实施仅适用于应用程序某些部分的身份验证或授权检查非常有用。您可以使用 beforeEnter
选项直接在路由配置中定义每个路由守卫。
beforeEnter
beforeEnter
守卫类似于 beforeEach
,但它仅在导航到特定路由时执行。它接收相同的三个参数:to
、from
和 next
。
import { createRouter, createWebHistory } from 'vue-router';
const routes = [
{ path: '/', component: () => import('../components/Home.vue') },
{
path: '/profile',
component: () => import('../components/Profile.vue'),
beforeEnter: (to, from, next) => {
const isAuthenticated = localStorage.getItem('token'); // Replace with your actual authentication check
if (!isAuthenticated) {
// Redirect to login page if user is not authenticated
next('/login');
} else {
// Proceed to the route
next();
}
}
},
{ path: '/login', component: () => import('../components/Login.vue') }
];
const router = createRouter({
history: createWebHistory(),
routes
});
export default router;
在此示例中,我们为 /profile
路由定义了一个 beforeEnter
守卫。此守卫检查用户是否经过身份验证,如果未通过身份验证,则将其重定向到 /login
路由。
组件内守卫允许你直接在 Vue 组件中定义守卫。这对于实现依赖于组件状态的守卫或执行特定于组件的任务很有用。Vue Router 提供了三种类型的组件内守卫:beforeRouteEnter
、beforeRouteUpdate
和 beforeRouteLeave
。
beforeRouteEnter
在确认渲染此组件的路由之前调用 beforeRouteEnter
守卫。它_无权_访问此
组件实例,因为在调用 guard 时尚未创建该组件。但是,您可以在下一个
回调中访问 component 实例。
<template>
<div>
<h1>Profileh1>
<p>Welcome, {{ user.name }}!p>
div>
template>
<script>
export default {
data() {
return {
user: null
};
},
beforeRouteEnter(to, from, next) {
// Fetch user data before entering the route
fetch('/api/user')
.then(response => response.json())
.then(user => {
// Pass the user data to the component instance
next(vm => {
vm.user = user;
});
})
.catch(error => {
console.error('Error fetching user data:', error);
next('/login'); // Redirect to login on error
});
}
};
script>
在此示例中:
Profile
组件中定义了一个 beforeRouteEnter
守卫。next
回调将用户数据传递给组件实例。beforeRouteUpdate
当渲染此组件的路由被更新时,将调用 beforeRouteUpdate
守卫,但该组件被重用。使用动态路由参数时,可能会发生这种情况。它_确实_可以通过 this
访问组件实例。
<template>
<div>
<h1>User Profileh1>
<p>User ID: {{ userId }}p>
<p>Name: {{ user.name }}p>
div>
template>
<script>
export default {
props: ['id'],
data() {
return {
userId: this.id,
user: null
};
},
watch: {
$route(to, from) {
// React to route changes and update the user data
this.userId = to.params.id;
this.fetchUser();
}
},
beforeRouteUpdate(to, from, next) {
// Fetch user data before updating the route
this.fetchUser()
.then(() => {
next(); // Proceed to update the route
})
.catch(error => {
console.error('Error fetching user data:', error);
next('/login'); // Redirect to login on error
});
},
methods: {
async fetchUser() {
try {
const response = await fetch(`/api/users/${this.userId}`);
const user = await response.json();
this.user = user;
} catch (error) {
console.error('Error fetching user data:', error);
throw error;
}
}
},
mounted() {
this.fetchUser();
}
};
script>
在此示例中:
UserProfile
组件中定义了一个 beforeRouteUpdate
守卫。beforeRouteLeave
当渲染此组件的 route 即将被离开时,将调用 beforeRouteLeave
守卫。它可以用来防止用户在不满足某些条件时离开路由,例如未保存的更改。它_确实_可以通过 this
访问组件实例。
<template>
<div>
<h1>Edit Profileh1>
<textarea v-model="profileData">textarea>
<button @click="saveChanges">Save Changesbutton>
div>
template>
<script>
export default {
data() {
return {
profileData: '',
hasUnsavedChanges: false
};
},
watch: {
profileData(newValue, oldValue) {
this.hasUnsavedChanges = newValue !== oldValue;
}
},
beforeRouteLeave(to, from, next) {
if (this.hasUnsavedChanges) {
const confirmLeave = window.confirm('You have unsaved changes. Are you sure you want to leave?');
if (confirmLeave) {
next(); // Allow leaving the route
} else {
next(false); // Prevent leaving the route
}
} else {
next(); // Allow leaving the route
}
},
methods: {
saveChanges() {
// Save the changes to the server
this.hasUnsavedChanges = false;
}
}
};
script>
在此示例中:
EditProfile
组件中定义了一个 beforeRouteLeave
守卫。