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

Vue Router拦截器是干啥的?咋用它解决项目问题?

terry 8小时前 阅读数 9 #Vue

很多做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前端网发表,如需转载,请注明页面地址。

发表评论:

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

热门