← 返回题目列表

Vue Router 导航守卫有哪些?完整导航流程是什么?

高频 中等 第 13 / 27 题 更新于 2026/07/29
VueVue 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 中常用 onBeforeRouteLeaveonBeforeRouteUpdate。 比如表单未保存时离开页面,需要询问用户。

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 的错误。

八、加强记忆

这题用“三类守卫 + 一条流程线”来记。三类守卫是全局、路由独享、组件内;流程线是离开旧组件、检查全局和路由、解析组件、确认导航、执行后置副作用。回答时再用登录鉴权和表单离开确认两个例子,就能覆盖大多数追问。