Vue Router拦截器是干啥的?咋用它解决项目问题?
很多做Vue项目的同学,碰到页面权限控制、跳转前埋点这类需求时,都会疑惑“Vue Router拦截器是干啥的?咋用它解决项目问题?” 这篇文章就用问答形式,把Router拦截器的作用、用法、实战场景掰开了讲明白。
Vue Router拦截器到底是什么?
可以先反问:“听说过‘导航守卫’吗?Router拦截器其实就是Vue Router里的导航守卫体系,用来在路由跳转的不同阶段插‘钩子’做逻辑处理。”
打个比方:你从首页跳转到个人中心,整个跳转过程像坐地铁进站——全局守卫像地铁入口的安检(所有路由跳转都要经过),路由独享守卫像某条线路的闸机(只拦截特定路由),组件内守卫像车厢门口的乘务员(针对单个组件的进入/离开)。
Vue Router把导航过程拆成了多个阶段,每个阶段提供对应的钩子函数:
- 全局守卫:
router.beforeEach
(跳转前)、router.beforeResolve
(组件解析前,全局最后一个守卫)、router.afterEach
(跳转完成后); - 路由独享守卫:在路由配置里写
beforeEnter
,只对当前路由生效; - 组件内守卫:
beforeRouteEnter
(进入组件前,组件实例还没创建)、beforeRouteUpdate
(路由参数变了但组件复用,/user/1
跳/user/2
)、beforeRouteLeave
(离开组件前)。
哪些项目需求得靠Router拦截器解决?
做项目时碰到“用户没登录就进不了个人中心”“表单没保存跳页要提醒”这类需求,Router拦截器就是最优解!分享几个高频场景:
权限控制:拦住建没权限的路
需求:用户必须登录才能进订单页、个人中心。
做法:给需要权限的路由加meta.requiresAuth
标记,用全局前置守卫beforeEach
统一拦截,代码示例:
router.beforeEach((to, from, next) => { // to:要去的目标路由;from:从哪来;next:决定往哪走的函数 if (to.meta.requiresAuth && !isLogin()) { // isLogin() 是自己封装的判断登录态方法(比如查localStorage里的token) next('/login'); // 没登录就打回登录页 } else { next(); // 正常放行 } });
路由配置里给需要权限的页面加标记:
{ path: '/order', component: Order, meta: { requiresAuth: true } // 元信息标记权限 }
页面跳转前:拦住“鲁莽”操作
需求:用户填了一半表单,点别的路由时要提醒“表单没保存,确定离开?”
做法:用组件内守卫beforeRouteLeave
,在组件里直接写逻辑:
export default { data() { return { formChanged: false } }, beforeRouteLeave(to, from, next) { if (this.formChanged) { // 假设formChanged记录表单是否修改 if (window.confirm('表单没保存,确定离开?')) { next(); // 点确定就放行 } else { next(false); // 点取消就留在当前页 } } else { next(); // 没修改就直接走 } } }
埋点统计:悄悄记下来访数据
需求:统计每个页面的访问次数(PV)。
做法:用全局后置守卫afterEach
,跳转完成后发请求统计:
router.afterEach((to, from) => { // sendAnalytics是自己封装的埋点函数,传页面路径 sendAnalytics(to.path); });
因为afterEach
在导航完成后执行,不会阻塞跳转,适合做这类“事后统计”。
让页面“会说话”
需求:不同页面显示不同标题(比如订单页显示“我的订单”,首页显示“首页”)。
做法:用全局前置守卫beforeEach
结合路由元信息:
router.beforeEach((to, from, next) => { // 从目标路由的meta里拿title,没有就用默认 document.title = to.meta.title || '我的App'; next(); });
路由配置里加标题:
{ path: '/order', component: Order, meta: { title: '我的订单' } }
用Router拦截器容易踩哪些“坑”?
“明明写了拦截逻辑,为啥路由卡着不动?多个守卫一起用咋顺序乱了?” 这部分讲常见错误和解决方法。
next()
调用不规范:路由“卡死”
错误示例:忘记调next()
,或者在一个守卫里多次调next
。
router.beforeEach((to, from, next) => { if (to.meta.requiresAuth) { // 这里漏了next()!路由会一直pending checkAuth(); } else { next(); } });
解决:每个分支都要调next()
,且一个守卫里next
只能调一次,正确写法:
router.beforeEach((to, from, next) => { if (to.meta.requiresAuth) { checkAuth().then(res => { if (res.success) next(); // 有权限就放行 else next('/login'); // 没权限跳登录 }); } else { next(); // 记得放行! } });
守卫执行顺序搞混:逻辑冲突
问题:全局守卫、路由独享守卫、组件内守卫一起用,谁先执行?
执行顺序是:
全局beforeEach
→ 路由独享beforeEnter
→ 组件内beforeRouteEnter
→ 全局beforeResolve
→ 组件渲染 → 全局afterEach
。
比如做权限控制时,要是全局守卫已经把未登录用户拦了,路由独享守卫里的权限逻辑可能永远不会触发,所以权限逻辑尽量集中在全局守卫,减少多层拦截的复杂度。
异步操作没处理好:守卫“失效”
需求:权限验证需要发请求(比如后端查用户角色),但异步请求还没结果,路由就跳转了。
错误示例:把next()
写在异步外面,导致请求还没响应就放行。
router.beforeEach((to, from, next) => { if (to.meta.requiresAuth) { // 异步请求还没结果,next()就执行了 api.checkAuth().then(res => { ... }); next(); } else { next(); } });
解决:把next()
放进异步回调 ,用async/await
更清爽:
router.beforeEach(async (to, from, next) => { if (to.meta.requiresAuth) { const res = await api.checkAuth(); // 等请求结果 if (res.success) next(); else next('/login'); } else { next(); } });
实战:用拦截器搭权限管理系统(模拟后台项目)
光说不练假把式!咱模拟一个“后台管理系统”,用Router拦截器实现“没登录进不了管理页、登录后跳回之前想去的页面”。
步骤1:配置路由(带权限标记)
// router.js import Vue from 'vue'; import Router from 'vue-router'; import Login from './views/Login.vue'; import Home from './views/Home.vue'; import User from './views/User.vue'; import NotFound from './views/NotFound.vue'; Vue.use(Router); const routes = [ { path: '/login', component: Login, meta: { requiresAuth: false } }, { path: '/', component: Home, meta: { requiresAuth: false } }, { path: '/user', component: User, meta: { requiresAuth: true } }, // 需要登录 { path: '*', component: NotFound } // 404页面 ]; const router = new Router({ routes }); export default router;
步骤2:全局守卫做登录态拦截
// 继续在router.js里写 import { getToken } from './utils/auth'; // 假设是从localStorage拿token的工具函数 router.beforeEach((to, from, next) => { const requiresAuth = to.meta.requiresAuth; const token = getToken(); // 比如localStorage.getItem('token') if (requiresAuth) { if (token) { // 这里可以加“验证token是否有效”的接口请求,比如后端校验 next(); // 有token就放行 } else { // 没token,跳登录页,并把“想去的页面”存在query里 next({ path: '/login', query: { redirect: from.path } }); } } else { next(); // 不需要权限的页面,直接放行 } });
步骤3:登录页处理“跳回原页面”
// Login.vue export default { methods: { handleLogin() { // 假设这里调登录接口,成功后存token api.login(this.username, this.password).then(res => { if (res.success) { setToken(res.token); // 存token到localStorage // 从路由参数里拿redirect,没有就跳首页 const redirect = this.$route.query.redirect || '/'; this.$router.push(redirect); // 跳回之前想去的页面 } }); } } }
步骤4:处理404(路由拦截延伸)
因为路由最后配了{ path: '*', component: NotFound }
,所以当用户访问不存在的路径时,会触发这个路由,如果需要更精细的拦截(比如判断用户权限后跳403),可以在全局守卫里加逻辑:
router.beforeEach((to, from, next) => { // ...之前的权限逻辑... // 新增:如果是404路由,判断是否登录,没登录跳登录,登录了才显示404 if (to.path === '*') { if (token) { next(); // 登录了就显示404 } else { next('/login'); // 没登录就跳登录 } } else { next(); } });
不用Router拦截器,还有替代方案吗?
“要是不用导航守卫,能实现权限控制、埋点这些需求吗?”
可以试试,但效率低。
- 用Vuex结合
$route
监听:在Vuex里写全局监听,每次路由变化时判断权限,但代码分散,不如守卫和路由系统紧耦合; - 用全局mixin:在mixin里写
beforeRouteEnter
之类的逻辑,但mixin适合复用组件逻辑,处理路由导航还是守卫更直接;
所以导航守卫是Vue Router为路由流程定制的解决方案,和路由生命周期深度绑定,逻辑更清晰、性能更优。
最后总结下:Vue Router拦截器(导航守卫)是处理路由“前中后”逻辑的核心工具,不管是权限、体验还是数据统计,都能通过不同守卫精准拦截,关键是理解「守卫执行顺序」「next的正确调用」「异步逻辑处理」这几个点,多写Demo踩踩坑,自然就得心应手啦~要是你还有具体场景搞不定,评论区留言,咱一起拆解!
版权声明
本文仅代表作者观点,不代表Code前端网立场。
本文系作者Code前端网发表,如需转载,请注明页面地址。
发表评论:
◎欢迎参与讨论,请在这里发表您的看法、交流您的观点。