Code前端首页关于Code前端联系我们

一、vue-router路由守卫到底是干啥的?

terry 3周前 (09-08) 阅读数 46 #Vue
文章标签 router;路由守卫

咱做Vue项目的时候,路由跳转这块经常要搞权限控制、数据加载、离开提示这些事儿,vue-router的「路由守卫」就是解决这些问题的好帮手!但刚接触的同学往往一头雾水:路由守卫到底是干啥的?分哪几类?咋用才不会踩坑?今天咱用白话+实际例子,把路由守卫的核心知识和常见问题一次性讲透~

你可以把路由守卫理解成「路由的门卫」—— 当你从页面A跳转到页面B时,整个跳转过程会经过好几个“检查点”,每个检查点都能插一段逻辑:比如判断用户有没有登录、提前加载页面数据、离开时提醒保存……

举个生活里的例子:你进小区(路由跳转),小区大门(全局守卫)先查你有没有门禁卡(登录态);要是去快递柜(某个路由),快递柜旁的保安(路由独享守卫)再查你有没有取件码(特殊权限);进家门(组件内)前,还得确认门关好没(组件内守卫,离开时检查)。

本质上,路由守卫是在「路由生命周期」的关键节点植入逻辑,让路由跳转的每一步都能被控制,解决权限、数据、交互这些实际问题。

路由守卫分哪些类型?各自啥时候触发?

路由守卫分三大类:全局守卫、路由独享守卫、组件内守卫,不同类型的触发时机、作用范围完全不一样,咱一个个拆:

全局守卫:管整个应用的所有路由

全局守卫作用于所有路由跳转,不管你从哪跳到哪,都会触发,常见的有3个钩子:

  • beforeEach:路由跳转前触发(最常用!),用户没登录就想进个人中心?拦下来跳登录页”。
  • beforeResolve:在 beforeEach、路由独享守卫、组件内守卫 之后,afterEach 之前触发,相当于“最后一次全局拦截机会”,适合做全局的最终校验。
  • afterEach:路由跳转完成后触发,适合做“收尾活”,比如关闭加载动画、埋点统计。

举个全局beforeEach的例子(登录拦截):

// 在main.js里配置全局守卫
router.beforeEach((to, from, next) => {
  const isLogin = localStorage.getItem('token') // 从本地取登录态
  if (to.path === '/login') { 
    next() // 要去登录页,直接放行
  } else {
    isLogin ? next() : next('/login') // 没登录就跳登录页
  }
})

这里to是“要去的路由”,from是“从哪来的路由”,next是“放行/跳转到哪”的函数。

路由独享守卫:只管某一个路由

路由独享守卫叫beforeEnter只作用于某一个具体路由,后台管理页只有管理员能进”,就在这个路由的配置里写守卫。

举个路由独享的例子(管理员页面权限):

// 路由配置文件(比如router/index.js)
{
  path: '/admin',
  component: Admin,
  beforeEnter: (to, from, next) => {
    const role = localStorage.getItem('role') 
    role === 'admin' ? next() : next('/403') // 不是管理员跳403页面
  }
}

只有访问/admin时,这个守卫才会触发,其他路由完全不管~

组件内守卫:管当前组件的路由变化

组件内守卫是写在组件内部的钩子,针对“当前组件对应的路由”的变化,常见的有3个:

  • beforeRouteEnter进入该组件对应的路由前触发(此时组件还没创建,拿不到this)。
  • beforeRouteUpdate:当前组件对应的路由参数变化时触发(/user/1 → /user/2,组件复用没销毁)。
  • beforeRouteLeave离开该组件对应的路由前触发(比如从详情页跳列表页,离开前提示)。

举个beforeRouteEnter的例子(进页面前加载数据):

// Detail.vue(商品详情组件)
export default {
  name: 'Detail',
  data() { return { goodsData: {} } },
  beforeRouteEnter(to, from, next) {
    // 发请求拿商品数据(to.params.id是路由参数)
    getGoodsDetail(to.params.id).then(res => {
      // 组件还没创建,this拿不到,所以用next的回调传数据
      next(vm => {
        vm.goodsData = res.data // vm是组件实例,给data赋值
      })
    })
  }
}

路由守卫里的next函数,为啥总踩坑?

next是路由守卫的核心函数,控制“放不放行、往哪放行”,但新手很容易在这翻车,咱总结3个高频坑:

坑1:多次调用next

next只能调用一次!如果在逻辑里写了多个next,会触发“NavigationDuplicated”错误。

反例(错误写法):

router.beforeEach((to, from, next) => {
  if (to.path === '/login') {
    next() // 第一次调用next
  }
  next() // 又调用一次,报错!
})

正确写法:用if-else确保next只执行一次:

router.beforeEach((to, from, next) => {
  if (to.path === '/login') {
    next() 
  } else {
    next('/login')
  }
})

坑2:异步操作没等完就next

如果守卫里有异步操作(比如发请求判断权限),没等异步完成就调用next,会导致判断逻辑“失效”。

反例(错误写法,没等请求完就next):

router.beforeEach((to, from, next) => {
  checkLogin().then(isLogin => { // checkLogin是异步函数
    if (!isLogin) next('/login')
  })
  next() // 这里会直接放行,不管checkLogin的结果
})

正确写法(用async/await等异步完成):

router.beforeEach(async (to, from, next) => {
  const isLogin = await checkLogin() // 等异步结果
  if (to.meta.requiresAuth && !isLogin) {
    next('/login')
  } else {
    next()
  }
})

坑3:在beforeRouteEnter里直接用this

beforeRouteEnter触发时,组件还没创建,所以thisundefined,必须通过next(vm => {})才能拿到组件实例。

反例(错误写法,直接用this):

beforeRouteEnter(to, from, next) {
  this.goodsData = {} // this是undefined,报错!
  next()
}

正确写法(用next的回调):

beforeRouteEnter(to, from, next) {
  getGoodsDetail(to.params.id).then(res => {
    next(vm => {
      vm.goodsData = res.data // vm是组件实例
    })
  })
}

路由守卫在实际项目里能解决哪些问题?

路由守卫不是花里胡哨的功能,而是实实在在解决开发痛点的,咱列几个高频场景:

权限控制(最核心场景)

  • 全局拦截:用beforeEach拦所有页面,判断登录态、角色,没登录就跳登录页,普通用户不让进管理员页”。
  • 单个路由拦截:用beforeEnter拦特殊页面(比如支付页必须先下单,否则跳订单页)。

数据预加载

进页面之前把数据请求好,避免页面“闪白”。

  • 详情页用beforeRouteEnter发请求,把数据传给组件。
  • 列表页用beforeEach提前请求列表数据,存到vuex里。

页面过渡与埋点

  • 加载动画:全局afterEach里关闭加载动画,比如路由跳转时显示loading,跳完关掉:
    router.afterEach(() => {
      store.commit('hideLoading') // 调用vuex的mutation关loading
    })
  • 埋点统计afterEach里记录用户访问路径,传给后端统计。

离开页面确认

表单页用beforeRouteLeave,判断用户是否修改了表单,没保存就弹窗提示:

beforeRouteLeave(to, from, next) {
  if (this.formEdited) { // formEdited是判断表单是否修改的标记
    const confirm = window.confirm('表单没保存,确定离开?')
    confirm ? next() : next(false) // 确定就放行,否则留在当前页
  } else {
    next()
  }
}

路由参数变化时更新数据

比如用户页/user/:id,id从1变2时,组件会复用(不会销毁重建),这时用beforeRouteUpdate重新请求数据:

beforeRouteUpdate(to, from, next) {
  this.getUserData(to.params.id) // 调用方法,拿新id的数据
  next()
}

和其他路由拦截方式比,路由守卫优势在哪?

有人会问:“我自己在组件里写watch $route也能拦截路由变化,为啥用路由守卫?” 咱对比下优势:

  1. 粒度更细:全局、单个路由、组件内,不同层级想拦就拦,比如全局守卫管所有页面,路由独享管单个页面,组件内管当前组件,灵活度拉满。
  2. 流程更顺:路由跳转的“生命周期”里每个阶段(跳转前、跳转中、跳转后)都有对应钩子,能精准控制,比如beforeEach拦在最前面,afterEach收尾,逻辑不打架。
  3. 结合Vue生态更丝滑:和组件生命周期、vuex配合自然,比如在beforeRouteEnter里调vuex的action拿数据,再传给组件。
  4. 错误处理更方便:通过next(false)或跳错误页(如403、404),统一处理“权限不足”“页面不存在”等情况,不用在每个组件里写重复逻辑。

学路由守卫前,得先掌握哪些基础?

想玩转路由守卫,得先把这些基础打牢:

  1. vue-router基本使用:会配路由(动态路由、嵌套路由),会用<router-link>router.push跳转。
  2. 组件生命周期:清楚createdmounted这些钩子的执行顺序,因为组件内守卫和生命周期有先后(比如beforeRouteEntercreated之前触发)。
  3. JS异步操作:懂async/awaitPromise,因为守卫里经常要处理异步请求(比如判断登录态、拉取数据)。
  4. 路由元信息(meta):给路由加meta字段(比如meta: { requiresAuth: true }),在守卫里通过to.meta.requiresAuth判断是否需要权限。

新手常见疑问集中解答

最后解决几个新手高频疑问,帮你少走弯路:

全局守卫和组件内守卫的执行顺序是啥?

比如从A组件跳B组件,执行顺序是:
全局beforeEach → (如果B路由有beforeEnter路由独享beforeEnter全局beforeResolveB组件的beforeRouteEnter全局afterEach

简单说:全局守卫是最外层,然后是路由独享,最后是组件内守卫

beforeRouteEnter里拿不到this,除了next(vm => {})还有别的办法吗?

可以把数据存vuex或全局变量,但next(vm => {})是最直接的方式——因为它会等组件创建好后,再把数据传给组件实例,逻辑最顺。

afterEach里能阻止路由跳转吗?

不能!afterEach路由跳转完成后触发的,所以没有next函数,只能做“收尾操作”(比如关loading、埋点),没法拦截跳转。

路由守卫里能改路由参数吗?

可以!在next里传新的路由配置,

next({ 
  path: '/newPath', 
  query: { tab: 'goods' }, // 传查询参数
  params: { id: 123 } // 传动态参数
})

这相当于“中断当前跳转,重新发起一个路由跳转”。

折腾一圈下来,你会发现路由守卫就是vue-router给咱的「路由控制权」—— 从全局到单个路由再到组件内,想在哪拦就在哪拦,想干啥逻辑就干啥逻辑~ 关键是把每个守卫的触发时机、next的用法吃透,再结合项目里的权限、数据、交互需求,就能玩得很溜,要是刚开始练,建议先从全局beforeEach做登录拦截入手,再慢慢拓展到组件内守卫处理表单保存这些细节,多写几个例子自然就通啦~

版权声明

本文仅代表作者观点,不代表Code前端网立场。
本文系作者Code前端网发表,如需转载,请注明页面地址。

发表评论:

◎欢迎参与讨论,请在这里发表您的看法、交流您的观点。

热门