Vue-路由守卫

110 阅读3分钟

前言

在 Vue 项目中,路由守卫是控制页面权限、处理跳转逻辑的核心。无论是全局登录鉴权,还是组件内的特殊状态判断,熟练掌握路由守卫都是前端开发的必备技能。本文将从基础概念出发,带你拆解路由守卫的分类及其在企业级项目中的实战应用。

一、 路由守卫全解析

路由守卫的核心作用是在路由跳转过程中执行特定的逻辑(如权限校验、数据预取)。

1. 全局路由守卫

全局守卫直接挂载在 router 实例上,对应用内的所有路由跳转生效。

  • beforeEach(to, from, next)最常用。在路由跳转前触发,常用于登录状态校验。
  • beforeResolve(to, from, next) :在导航被确认之前,且组件内守卫和异步路由组件被解析之后调用。
  • afterEach(to, from) :在路由跳转完成后触发。注意: 它不接受 next 函数,也不会改变导航。

参数说明:

  • to: 即将要进入的目标路由对象。
  • from: 当前正要离开的路由对象。
  • next: 决定导航的行为。next() 放行;next(false) 中止;next('/path') 重定向。

2. 组件内的路由守卫

直接定义在 .vue 组件内部,针对特定组件生效。

  • beforeRouteEnter:路由进入该组件前调用。此时拿不到组件实例 this,因为组件实例还没被创建。该钩子在全局守卫beforeEach之后,全局守卫afterEach之前调用。
  • beforeRouteUpdate:当前路由改变,且该组件被复用时调用(例如 /user/1 跳转到 /user/2)。
  • beforeRouteLeave:导航离开该组件时调用。常用于“离开前未保存”的选择提醒。

二、 使用示例:权限鉴权流

在真实项目中,我们通常将路由配置与守卫逻辑拆分。

1. 路由实例配置 (router/index.ts)

import { createRouter, createWebHashHistory, RouteRecordRaw } from 'vue-router'
import staticRoutes from './routes'

const router = createRouter({
  history: createWebHashHistory(),
  routes: staticRoutes as RouteRecordRaw[],
  // 关键:切换页面后自动滚动到顶部
  scrollBehavior: () => ({ left: 0, top: 0 })
})

export default router

2. 路由表定义 (router/routes.ts)

import { RouteRecordRaw } from 'vue-router'

const routes: RouteRecordRaw[] = [
  {
    path: '/login',
    name: 'xxxx-登录',
    component: () => import('@/views/login/index.vue')
  },
  {
    path: '/',
    component: () => import('@/views/layout/index.vue'),
    redirect: '/home',
    children: [
      {
        path: 'home', // 建议子路由使用相对路径
        name: 'xxxxx-主页',
        component: () => import('@/views/home/index.vue')
      }
    ]
  }
]

export default routes

3. 全局权限校验 (permission.ts)

main.ts 中引入此文件,确保守卫逻辑生效。

import router from './router'
import { getAccessToken } from '@/libs/utils/auth'
import { getPublicToken } from '@/libs/utils/dmAuth'
import { DM_SITEID } from '@/config/assistant'

// 路由白名单:不需要登录即可访问
const whiteList: string[] = ['/login']

// 全局前置守卫
router.beforeEach(async (to, from, next) => {
  const token = getAccessToken()
  const publicToken = getPublicToken(DM_SITEID)

  // 判断是否具备双重 Token(业务 Token 与 对话管理 Token)
  if (token && publicToken) {
    if (to.path === '/login') {
      next({ path: '/' }) // 已登录状态访问登录页,直接重定向到首页
    } else {
      next()
    }
  } else {
    // 处理未登录情况
    if (whiteList.includes(to.path)) {
      next() // 白名单页面,直接放行
    } else {
      // 携带当前页面地址,方便登录后回跳
      next(`/login?redirect=${to.fullPath}`)
    }
  }
})

// 全局后置守卫
router.afterEach((to) => {
  // 动态修改页面标题
  if (to.name) {
    document.title = to.name as string
  }
  window.scrollTo(0, 0)
})