Vue3路由守卫只会写死拦截?一文带你从入门到权限验证/数据预加载全玩明白
之前做电商后台重构,把Vue2转成Vue3后,第一个踩的大雷就是路由守卫!一开始照搬Vue2的beforeEach,写了个硬邦邦的token判断,结果用户刷新页面直接白屏半天,权限按钮明明配置了还是能点跳转空页面——后来翻了Vue3的官方文档,又结合了几个大厂的开源项目,才把坑填得差不多,顺带解锁了好多之前不会的用法,比如数据预加载、路由跳转时的模态框取消、嵌套路由的精准拦截这些,今天就把踩过的坑和整理的干货全分享出来,不管你是刚学Vue3的小白,还是转框架的老手,看完应该都能用上。
路由守卫到底是个啥?和“门卫大爷”有啥区别?
你可以把Vue的路由理解成公司的内部办公系统入口,每个路由就是不同的办公区域——CEO办公室、财务部、研发部茶水间这种,那路由守卫呢?就是这些区域门口的“保安大叔+智能门禁+预约登记员”的结合体: 保安大叔:有权限卡才让进(也就是路由鉴权) 智能门禁:能提前知道谁要来、要去哪,甚至能临时改去的地方(导航守卫的钩子逻辑) 预约登记员:去之前先让预约部门准备好茶水文件(数据预加载) 离开之前提醒别忘了拿东西(比如未保存的表单弹窗) 哦对了,和现实里的门卫不一样的是,Vue3的守卫有不同的“站岗位置”:有的在公司大门口(全局守卫),不管你去哪个区域都要先查;有的在每个区域门口(组件内守卫/路由独享守卫),只有进特定区域才用;还有的是你进门选好楼层区域后,电梯里的确认按钮(解析守卫),主要用来处理异步路由的细节。
Vue3的路由守卫有哪几种?具体站在哪?
先给大家列个表格?不对,咱们用大白话讲位置更清楚:
- 全局前置守卫 beforeEach:站在公司大门安检处,不管你从哪来、要去哪,所有路由跳转都必须过这关——也是我们最常用的,用来做全局token校验、页面访问白名单/黑名单、登录状态过期跳转登录页这些。
- 全局解析守卫 beforeResolve:站在安检后、门禁卡刷开前的等待区——也是所有跳转都会触发,但比beforeEach晚,比组件内的beforeRouteEnter早,什么时候用呢?比如异步组件/动态路由加载完了再最后确认一遍权限,或者加载一些全局都要用但只有跳转到具体页面才需要的资源,比如切换到“数据分析页”才临时加载ECharts的CDN?
- 全局后置钩子 afterEach:站在办公区域门口的打卡机旁边——已经放你进去了,不能再改跳转目的地了,只能做一些收尾的工作,比如修改页面的标题(SEO友好!很多做官网的都用这个)、埋点记录用户的访问路径、关闭全局加载动画这些。
- 路由独享守卫 beforeEnter:站在特定办公区域(比如CEO办公室)门口,只有去这个路由才会触发——比全局守卫省资源,适合给单个或几个路由加特殊的权限,比如只有管理员才能访问的“用户管理/系统设置”,单独加一个判断adminRole的beforeEnter就行,不用在全局beforeEach里写一堆if else分支。
- 组件内守卫:站在某个具体的工位或者会议室门口——只能管当前这个组件的路由跳转,又分成三个:
- beforeRouteEnter:进这个工位前触发——注意哦,这时候组件还没挂载,this是undefined!别想在这直接改组件里的data,怎么办?后面有个小技巧给大家讲。
- beforeRouteUpdate:你已经坐在A工位了,现在要调去同部门的B工位(比如路由的路径参数变了,但组件复用了,像电商的商品详情页,从id=1跳到id=2,ProductDetail组件不会销毁重建)——这时候触发beforeRouteUpdate,可以用来重新加载新的商品数据。
- beforeRouteLeave:你要离开当前这个工位/会议室——这时候组件还没销毁,this可以用!最常用的场景就是检查表单有没有保存,如果没保存,弹出一个“您确定要离开吗?未保存的内容会丢失”的确认框,用户点“取消”就可以阻止跳转,继续编辑。
新手最容易踩的5个坑,我一个都没躲过!
重构电商后台的时候,这5个坑踩得我怀疑人生,差点又换回Vue2——还好后来找到了原因,现在分享出来帮大家避避:
坑1:beforeEach里忘加next()或者return了,导致路由直接卡死
很多新手学路由守卫的时候,可能会看到Vue2的写法都是用next(),然后就下意识照搬,但Vue3其实推荐用return代替next()——不管你用哪种,都必须要有一个“放行/跳转”的操作! 举个反面例子:
// Vue3的错误写法,路由直接白屏,因为没有放行或跳转
router.beforeEach((to, from) => {
const token = localStorage.getItem('token')
if (to.meta.requiresAuth && !token) {
// 这里只有判断,没有return '/login' 或者 next('/login')
console.log('请先登录')
}
})
Vue3的推荐写法(更简洁,符合组合式API的风格):
router.beforeEach((to, from) => {
const token = localStorage.getItem('token')
// 先看是不是去登录页/注册页这些白名单页面,如果是直接放行
if (['/login', '/register', '/404'].includes(to.path)) {
return true // 或者 return undefined,都表示放行
}
// 不是白名单,检查有没有token
if (!token) {
// 没有token,跳转到登录页,并且带上要去的页面路径,登录成功后直接跳转过去
return { path: '/login', query: { redirect: to.fullPath } }
}
// 有token,正常放行
return true
})
要是想用Vue2的next()也可以,但在Vue3的beforeEach里,next()只能调用一次!重复调用会报错,而且不如return直观。
坑2:beforeRouteEnter里用this,直接报undefined
刚才说了,beforeRouteEnter触发的时候,组件还没挂载,this根本拿不到——那要是我想在组件挂载前,根据路由参数预加载数据,然后塞到组件的data里怎么办? 别慌,beforeRouteEnter有一个回调函数next(vm),这个vm就是组件的实例!等组件挂载完之后,会自动执行这个回调,不过Vue3推荐用组合式API,那组合式API里有没有beforeRouteEnter的替代写法? 有!在
code前端网