深入解析 Vue Router 的 beforeEach:功能、用法与实践指南
- 什么是 beforeEach?
- 基本语法与参数解析
- next() 的 4 种调用方式
- 常见使用场景与代码示例
- 动态路由加载的实践技巧
- 常见陷阱与避坑指南
- 总结
1. 什么是 beforeEach?
beforeEach
是 Vue Router 提供的 全局前置守卫(Global Before Guards),在路由跳转 之前 触发。它的核心作用是:
- 拦截导航:在用户访问某个路由前进行权限校验、数据预加载等操作。
- 控制跳转行为:允许继续导航、重定向到其他页面,或直接中断跳转。
它是实现以下功能的关键机制:
- 登录状态验证
- 动态路由加载(如权限路由)
- 页面访问统计
- 路由切换动画控制
2. 基本语法与参数解析
语法结构
const router = new VueRouter({ ... });
router.beforeEach((to, from, next) => {
// 逻辑处理
next(); // 必须调用!
});
参数详解
参数 | 类型 | 说明 |
---|---|---|
to | Route | 目标路由对象,包含 path 、params 、query 、meta 等属性。 |
from | Route | 当前路由对象,即正要离开的路由。 |
next | Function | 必须调用的函数,决定是否放行、重定向或中断导航。 |
3. next() 的 4 种调用方式
3.1 放行导航
next();
- 行为:直接允许导航到
to
路由。 - 场景:无需拦截的公共页面(如首页、帮助页)。
3.2 中断导航
next(false);
- 行为:取消当前导航,用户停留在
from
路由。 - 场景:用户无权访问目标路由时阻止跳转。
3.3 重定向到其他路由
next('/login');
// 或
next({ path: '/login', query: { redirect: to.path } });
- 行为:强制跳转到指定路径。
- 场景:用户未登录时重定向到登录页,并记录原目标路径。
3.4 标记错误
next(new Error('Network Error'));
- 行为:导航被终止,错误会传递给
router.onError()
。 - 场景:处理异步操作中的异常(如接口请求失败)。
4. 常见使用场景与代码示例
4.1 登录状态验证
router.beforeEach((to, from, next) => {
const isLoggedIn = localStorage.getItem('token');
if (to.meta.requiresAuth && !isLoggedIn) {
next({ path: '/login', query: { redirect: to.fullPath } });
} else {
next();
}
});
- 说明:若目标路由需要登录(通过
meta.requiresAuth
标记),但用户未登录,则重定向到登录页。
4.2 动态路由加载(权限控制)
router.beforeEach(async (to, from, next) => {
if (用户已加载权限路由) {
next();
return;
}
try {
const roles = await fetchUserRoles(); // 异步获取用户角色
const accessRoutes = generateRoutes(roles); // 根据角色生成路由
router.addRoutes(accessRoutes); // 动态添加路由
next({ ...to, replace: true }); // 强制重新导航
} catch (error) {
next('/error');
}
});
- 说明:根据用户角色动态添加路由,并通过
next({ ...to, replace: true })
确保路由表更新。
4.3 页面访问统计
router.beforeEach((to, from, next) => {
logPageView(to.path); // 上报页面访问数据
next();
});
- 说明:在跳转前记录用户访问路径,用于数据分析。
5. 动态路由加载的实践技巧
5.1 解决异步路由加载的时序问题
动态添加路由后,直接调用 next()
可能导致目标路由尚未注册,需通过以下写法强制重新导航:
next({ ...to, replace: true });
- 原理:
{ ...to }
保留原始路由信息,replace: true
替换浏览器历史记录。 - 效果:确保路由表更新后重新触发导航流程。
5.2 避免路由重复添加
在路由守卫中添加状态标记,防止多次加载:
let isRoutesLoaded = false;
router.beforeEach((to, from, next) => {
if (isRoutesLoaded) {
next();
return;
}
loadRoutes().then(() => {
isRoutesLoaded = true;
next({ ...to, replace: true });
});
});
6. 常见陷阱与避坑指南
6.1 忘记调用 next()
router.beforeEach((to, from, next) => {
if (someCondition) {
// 忘记调用 next(),导航被挂起!
}
});
- 后果:页面卡住,无法跳转。
- 修复:确保所有逻辑分支都调用
next()
。
6.2 重定向循环
router.beforeEach((to, from, next) => {
if (to.path === '/login') {
next();
return;
}
if (!isLoggedIn) {
next('/login'); // 若未登录,重定向到登录页
} else {
next();
}
});
- 问题:若登录页本身需要权限(如
meta.requiresAuth: true
),会导致无限重定向。 - 修复:在路由配置中为登录页添加
meta: { requiresAuth: false }
。
7. 总结
核心要点
- 功能:
beforeEach
是路由跳转前的拦截器,用于权限控制、动态路由加载等。 - 关键方法:
next()
必须调用,且支持放行、重定向、中断等操作。 - 动态路由:通过
next({ ...to, replace: true })
解决异步加载时序问题。
最佳实践
- 明确路由权限:通过
meta
字段标记路由是否需要登录或特定角色。 - 避免副作用:确保守卫逻辑简洁,避免阻塞主线程。
- 错误处理:使用
next(error)
或try/catch
捕获异常。