Vue 路由守卫是 Vue Router 提供的一种功能,用于在路由导航的不同阶段插入控制逻辑,从而实现对路由跳转的精细化管理。以下是 Vue 路由守卫的详细解析:
路由守卫的分类
Vue 路由守卫主要分为三类:全局守卫、路由独享守卫和组件内守卫。
1. 全局守卫
全局守卫对整个应用中的所有路由跳转生效,主要包括以下几种:
- 全局前置守卫 (
beforeEach):在路由跳转之前被调用,可以用于身份验证、权限检查等。例如,检查用户是否登录,如果未登录则重定向到登录页面。
router.beforeEach((to, from, next) => {
const isAuthenticated = localStorage.getItem('token');
if (to.meta.requiresAuth && !isAuthenticated) {
next('/login');
} else {
next();
}
});
-
每个守卫接收两个(或三个)参数: to:即将要进入的目标 from:当前导航正要离开的路由 next:(可选)一个继续执行的函数
-
可以返回的值如下: false:取消当前的导航 一个路由地址:通过一个路由地址重定向到一个不同的地址,如同调用
router.push(),并且可以传入类似“replace:true” 或 “name:home” 之类的选项
router.beforeEach(async (to,from) => {
// 检查用户是否已登录
if ( !isAuthenticated && to.name !== 'Login') {
return {name : 'Login',replace : true}
}
});
-
全局解析守卫 (
beforeResolve):在路由确认后、组件渲染前被调用,通常用于数据预加载。router.beforeEach(async (to,from) => { // 检查用户是否一登陆 if ( to.meta.requiresAdmin) { try { await validateAdminPrivileges(); } catch (error) { return '/no-permission'; } } });router.beforeResolve是获取数据或执行其他操作(如果用户无法进入页面时你希望避免执行的操作)的理想位置 -
全局后置钩子 (
afterEach):在路由跳转完成后被调用,通常用于页面访问统计等。和守卫不同的事,这个钩子不会接受next函数也不会改变导航本身router.afterEach((to) => { analytics.trackPageView(to.fullPath); });
2. 路由独享守卫
路由独享守卫只对某个特定的路由生效,通过在路由配置中定义 beforeEnter 属性来实现。它适用于对特定路由进行特殊的权限验证或逻辑处理 。
值得注意的是: beforeEnter 旨在进入路由时触发,不会在 params 、 query 或 hash 改变时触发。
例如:从 /users/2 进入 /users/3 或者从 /users/2#info 进入到 /users/2#projects。 他们只有在 从一个不同的 路由导航时,才会触发
const routes = [
{
path: '/premium-content',
component: PremiumContent,
beforeEnter: (to, from, next) => {
if (!userStore.isSubscribed) {
next('/upgrade');
} else {
next();
}
}
}
];
3. 组件内守卫
组件内守卫定义在组件内部,只对该组件的路由跳转生效,主要包括以下几种:
beforeRouteEnter:在进入该组件的路由前执行,适合进行数据预加载或权限验证。export default { beforeRouteEnter(to, from, next) { fetchInitialData(to.params.id).then(data => { next(vm => vm.setData(data)); }); } };beforeRouteUpdate:在当前路由改变且该组件被复用时调用,适用于路由参数变化时的逻辑处理。export default { beforeRouteUpdate(to, from) { this.productId = to.params.id; this.loadProductData(); } };beforeRouteLeave:在离开该组件的路由前执行,常用于提示用户保存未保存的数据。export default { beforeRouteLeave(to, from, next) { if (this.unsavedChanges) { const confirm = window.confirm('有未保存的修改,确定离开吗?'); next(confirm); } else { next(); } } };
完整的导航解析流程
- 导航被触发
- 在失活的组件里调用 (
beforeRouteLeave)守卫。 - 调用全局的 (
beforeEach) 守卫。 - 在重用的组件里调用(
beforeRouteUpdate) 守卫。 - 在路由配置里调用 (
beforeEnter)。 - 解析异步路由组件
- 在被激活的组件里调用 (
beforeRouteEnter)。 - 调用全局的 (
beforeResolve)。 - 完成导航。
- 调用全局后置钩子 (
afterEach)。 - 触发dom 更新
- 调用 (
beforeRouteEnter)守卫传给 next 的回调函数,创建好的组件实例会作为回调函数的参数传入
最佳实践建议
- 权限校验链:建议使用全局守卫进行基础校验,路由独享守卫进行特定校验,组件守卫进行最终校验。
- 异步处理:在守卫中使用
async/await处理异步操作时,要确保正确调用next()。 - 性能优化:避免在全局守卫中执行耗时操作,必要时可结合路由懒加载。
- 调试技巧:使用
router.onError()捕获导航过程中的异常。