【Vue Router】路由模式、懒加载、守卫、权限、缓存

发布于:2025-08-09 ⋅ 阅读:(17) ⋅ 点赞:(0)

前言

Vue Router 是 Vue 生态中处理页面跳转的核心工具,它解决了单页应用中 URL 管理、组件切换、状态维护等关键问题,同时提供了丰富的功能(如动态路由、嵌套路由、路由守卫)。除了经常用到的路由配置以外,我们还需了解以下几点。

1. Vue 路由模式

Vue Router 支持两种路由模式:

模式 原理 特点 启用方式
Hash 模式 使用 URL hash (#) 模拟完整 URL http://example.com/#/home 兼容性好(支持 IE9) 无需服务器配置 mode: 'hash' (默认)
History 模式 基于 HTML5 History API http://example.com/home URL 更美观 需要服务器端支持 mode: 'history'

服务器配置示例 (History 模式):

# Nginx 配置
location / {
  try_files $uri $uri/ /index.html;
}

2. 路由懒加载

实现组件按需加载,提升首屏性能。

实现方式

const router = new VueRouter({
  routes: [
    // 魔法注释:webpackChunkName 定义分包名称
    { 
      path: '/dashboard',
      component: () => import(/* webpackChunkName: "dashboard"*/'./views/Dashboard.vue')
    },
    {
      path: '/user/:id',
      component: () => import(/* webpackChunkName: "user" */ './views/UserProfile.vue')
    }
  ]
})

优化效果

  • 首屏加载时间减少 30-50%
  • 按路由拆分 chunk 文件
  • 支持预加载:<link rel="prefetch">

3. 路由缓存

使用 <keep-alive> 缓存组件状态:

<template>
  <div id="app">
    <!-- 缓存带有 meta.keepAlive 的路由 -->
    <keep-alive :include="cachedViews">
      <router-view v-if="$route.meta.keepAlive"></router-view>
    </keep-alive>
    
    <!-- 不缓存的路由 -->
    <router-view v-if="!$route.meta.keepAlive"></router-view>
  </div>
</template>

<script>
export default {
  computed: {
    cachedViews() {
      return this.$store.state.cachedRoutes
    }
  }
}
</script>

路由配置

{
  path: '/user/:id',
  component: UserDetails,
  meta: { 
    keepAlive: true, // 启用缓存
    scrollPos: true  // 记录滚动位置
  }
}

可通过属性控制缓存范围:

  • include:仅缓存名称匹配的组件(组件需定义 name 属性)。
  • exclude:不缓存名称匹配的组件。
  • max:最多缓存的组件实例数量。

示例:

<keep-alive include="Home,About" :max="10">
  <router-view />
</keep-alive>

被缓存的组件会触发 activated(激活时)和 deactivated(失活时)生命周期钩子,替代 mountedunmounted

4. 路由守卫

守卫类型

// 1. 全局前置守卫
router.beforeEach((to, from, next) => {
  if (to.meta.requiresAuth && !isLoggedIn()) next('/login')
  else next()
})

// 2. 路由独享守卫
{
  path: '/admin',
  beforeEnter: (to, from, next) => {
    checkAdminPermission() ? next() : next('/403')
  }
}

// 3. 组件内守卫
const UserProfile = {
  beforeRouteEnter(to, from, next) {
    // 不能访问 this
    next(vm => console.log(vm.user)) // 通过回调访问组件实例
  },
  beforeRouteUpdate(to, from, next) {
    // 路由参数变化时触发
    this.fetchData(to.params.id)
    next()
  },
  beforeRouteLeave(to, from, next) {
    // 离开确认
    window.onbeforeunload = null
    next()
  }
}

1.全局路由

Route.beforeEeach 任意路由跳转前触发

Route.beforeResolve 导航确认前触发

Route.afterEach 导航完成后触发

2.路由独享守卫

beforeEnter 在路由配置上定义,进入路由时触发

3.组件内守卫

beforeRouteEnter 渲染组件路由被验证前调用(适合做进入前的权限验证,不能访问this)

beforeRouteUpdate 当组件路由改变,组件复用时候调用(适合在路由参数变化时重新加载数据

beforeRouteLeave 导航离开组件的对应路由时调用(适合做离开前的确认(如未保存的表单)

导航解析流程:

  • 1.触发导航 → 生成目标路由
  • 2.在失活的组件里调用(beforeRouteLeave
  • 3.执行 全局前置守卫beforeEach
  • 4.在重用的组件里调用beforeRouteUpdate
  • 5.执行 路由独享守卫beforeEnter
  • 6.执行 组件内前置守卫beforeRouteEnter
  • 7.解析异步路由组件:如 () => import('./User.vue')
  • 8.执行 组件内更新守卫beforeRouteUpdate,如适用)
  • 9.执行 组件内离开守卫beforeRouteLeave,如适用)
  • 10.执行 全局解析守卫beforeResolve
  • 11.确认导航,更新 URL 和路由记录
  • 12.执行 全局后置钩子afterEach
  • 13.Dom更新:销毁旧组件,创建并渲染新组件 → 完成导航

通过这个流程,Vue Router 实现了对路由导航的精细控制,结合不同类型的守卫可以满足权限验证、数据预加载、离开确认等多种业务需求。

5. 路由与后端菜单结合

动态路由工作流
在这里插入图片描述

实现代码

// 1. 定义基础路由(无需权限)
const constantRoutes = [
  { path: '/login', component: Login },
  { path: '/404', component: NotFound }
]

// 2. 从后端获取菜单
async function initRoutes() {
  const menuData = await axios.get('/api/user/menus')
  const dynamicRoutes = generateRoutes(menuData)
  
  // 3. 动态添加路由
  dynamicRoutes.forEach(route => router.addRoute(route))
  
  // 4. 添加404捕获
  router.addRoute({ path: '*', redirect: '/404' })
}

// 菜单转换函数
function generateRoutes(menuData) {
  return menuData.map(menu => ({
    path: menu.path,
    component: () => import(`@/views/${menu.component}`),
    meta: { title: menu.title, icon: menu.icon },
    children: menu.children ? generateRoutes(menu.children) : []
  }))
}

6. 路由权限控制

完整权限方案

// 权限检查函数
function hasPermission(route, roles) {
  if (route.meta?.roles) {
    return roles.some(role => route.meta.roles.includes(role))
  }
  return true // 无meta.roles则公开访问
}

// 路由过滤
function filterRoutes(routes, roles) {
  return routes.filter(route => {
    if (hasPermission(route, roles)) {
      if (route.children) {
        route.children = filterRoutes(route.children, roles)
      }
      return true
    }
    return false
  })
}

// 全局前置守卫
router.beforeEach(async (to, from, next) => {
  // 1. 获取用户角色
  const roles = store.getters.roles || []
  
  // 2. 白名单检查
  if (whiteList.includes(to.path)) return next()
  
  // 3. 未登录重定向
  if (!roles.length) return next(`/login?redirect=${to.path}`)
  
  // 4. 已登录但未初始化路由
  if (!store.getters.routesLoaded) {
    await initDynamicRoutes(roles) // 动态添加路由
    return next({ ...to, replace: true })
  }
  
  // 5. 检查目标路由权限
  if (!hasPermission(to, roles)) return next('/403')
  
  next()
})

按钮级权限控制

<template>
  <button v-permission="'user:create'">添加用户</button>
</template>
// 权限指令
Vue.directive('permission', {
  inserted(el, binding) {
    const { value } = binding
    const permissions = store.getters.permissions
    
    if (value && !permissions.includes(value)) {
      el.parentNode?.removeChild(el)
    }
  }
})

最佳实践总结

  1. 路由设计
    • 公共路由使用静态注册
    • 权限路由使用动态注册
    • 路由元信息存储权限标识
  2. 性能优化
    • 路由懒加载 + webpack 分包
    • 滚动行为恢复
    • 路由组件复用
  3. 安全控制
    • 前端路由权限校验
    • 后端接口二次验证
    • 按钮级权限控制
  4. 错误处理
    • 统一404处理
    • 403无权限页面
    • 路由切换错误捕获
// 错误捕获
router.onError(error => {
  console.error('路由错误:', error)
  if (/ChunkLoadError/.test(error.message)) {
    window.location.reload() // 重新加载解决chunk加载失败
  }
})

网站公告

今日签到

点亮在社区的每一天
去签到