Vue Router 导航守卫有哪些?完整导航流程是什么?
简化版
Vue Router 导航守卫分为全局守卫、路由独享守卫、组件内守卫,用来做登录鉴权、权限控制、数据预取、离开确认等。完整流程要理解“离开旧路由、进入新路由、解析异步组件、确认导航、更新 DOM”的顺序。
详细版
常见守卫包括:
router.beforeEach((to, from) => {});
router.beforeResolve((to, from) => {});
router.afterEach((to, from) => {});
路由独享:
{
path: '/admin',
component: Admin,
beforeEnter: (to, from) => {},
}
组件内:
onBeforeRouteLeave((to, from) => {});
onBeforeRouteUpdate((to, from) => {});
面试回答重点是:鉴权通常放全局前置守卫,单路由校验放 beforeEnter,组件离开确认放组件内守卫,日志统计可放 afterEach。
完整版教学
一、导航守卫解决的是路由切换过程控制
单页应用里,URL 变化不一定刷新页面。 Vue Router 需要在组件切换前后给开发者插入控制点。 这些控制点就是导航守卫。
用户点击 /admin
-> Router 准备切换
-> 检查登录状态和权限
-> 允许进入或重定向到 /login
如果没有守卫,未登录用户可能先看到管理页,再被业务代码赶走。 守卫能把“能不能进入”提前到路由层处理。 这也是它在面试中高频出现的原因。
二、全局守卫适合通用规则
全局守卫注册在 router 实例上,对每次导航都生效。
最常见的是 beforeEach 做登录鉴权,afterEach 做埋点或标题更新。
它适合全站统一规则,不适合塞太多单页面逻辑。
router.beforeEach((to) => {
const needLogin = to.meta.requiresAuth;
const isLogin = Boolean(localStorage.getItem('token'));
if (needLogin && !isLogin) {
return { path: '/login', query: { redirect: to.fullPath } };
}
});
假设 20 个页面都需要登录,如果每个页面自己判断,就会重复 20 份。
放到全局守卫后,只要维护 meta.requiresAuth。
这就是路由元信息和全局守卫配合的价值。
三、路由独享守卫适合单路由特殊规则
beforeEnter 写在路由配置上,只对进入该路由生效。
例如活动页必须检查活动是否开始,订单页必须检查订单 id 是否合法。
这类规则和某个路由强绑定,不适合放全局守卫。
{
path: '/activity/:id',
component: ActivityPage,
beforeEnter: (to) => {
if (!/^\\d+$/.test(String(to.params.id))) {
return '/404';
}
},
}
| 守卫位置 | 适合场景 | 例子 |
|---|---|---|
| 全局守卫 | 全站通用规则 | 登录、权限、标题、埋点 |
| 路由独享守卫 | 单路由进入规则 | 参数校验、活动状态 |
| 组件内守卫 | 和组件实例强相关 | 离开确认、复用组件更新 |
这样拆分后,守卫职责会更清楚。
四、组件内守卫适合处理组件状态
组件内守卫能访问组件相关状态。
在 Composition API 中常用 onBeforeRouteLeave 和 onBeforeRouteUpdate。
比如表单未保存时离开页面,需要询问用户。
const dirty = ref(false);
onBeforeRouteLeave(() => {
if (dirty.value) {
return window.confirm('内容未保存,确定离开吗?');
}
});
另一个高频点是同一个组件复用时,路由参数变了但组件不销毁。
例如 /user/1 切到 /user/2,可以用 onBeforeRouteUpdate 重新请求数据。
这比只依赖 mounted 更可靠。
五、完整导航流程要抓住几个关键阶段
完整流程不必死背所有细枝末节,但要知道大方向:先处理离开,再处理进入,最后确认并更新。 面试中可以用简化流程讲清楚。 这样既不会背错,也能解释为什么某些守卫拿不到新组件实例。
触发导航
-> 失活组件 beforeRouteLeave
-> 全局 beforeEach
-> 路由 beforeEnter
-> 解析异步组件
-> 组件 beforeRouteEnter / update
-> 全局 beforeResolve
-> 确认导航
-> afterEach
-> DOM 更新
afterEach 不能阻止导航,因为导航已经确认。
它更适合做页面统计、关闭进度条、修改标题这类副作用。
如果要阻止或重定向,应该在确认前的守卫里返回结果。
六、Vue Router 4 中推荐 return 而不是 next 滥用
Vue Router 4 支持在守卫里直接 return false、返回路径或返回路由对象。
这比老式 next() 更不容易出错。
老项目仍可能看到 next,但新项目推荐返回值。
router.beforeEach((to) => {
if (to.meta.requiresAuth && !isLogin()) {
return { name: 'login' };
}
return true;
});
记忆钩子:守卫不是越多越好,全局管共性,独享管单路由,组件内管组件状态。
常见事故是 next() 调了 2 次,导致导航行为混乱。
返回值写法可以让每个分支更明确,也更适合 TypeScript 检查。
七、常见误区与追问
- 误区:所有权限逻辑都应该写在全局守卫里。 全局守卫适合通用规则,页面级特殊校验应该下放到路由独享或业务层。
- 误区:afterEach 可以阻止跳转。
afterEach发生在导航确认之后,适合副作用,不适合拦截。 - 误区:组件 mounted 每次路由参数变化都会重新执行。 同组件复用时可能不会销毁重建,应使用
onBeforeRouteUpdate或监听参数。 - 追问:登录后如何回到原页面? 在重定向到登录页时带上
redirect=to.fullPath,登录成功后再跳回。 - 追问:导航守卫里适合请求接口吗? 可以做轻量鉴权或必要预取,但重接口容易拖慢路由切换,通常要有 loading 和失败策略。
- 追问:为什么 Vue Router 4 推荐返回值? 返回值比多分支
next()更清晰,能减少重复调用next的错误。
八、加强记忆
这题用“三类守卫 + 一条流程线”来记。三类守卫是全局、路由独享、组件内;流程线是离开旧组件、检查全局和路由、解析组件、确认导航、执行后置副作用。回答时再用登录鉴权和表单离开确认两个例子,就能覆盖大多数追问。