解耦的中型项目路由系统设计

路由系统说明文档

概述

按「路由类型拆分解耦」

核心思路:路由系统按职责划分为三类:核心路由、静态业务路由和动态业务路由,分别对应不同的权限需求和加载时机,通过以上路由系统设计,实现了权限与路由的解耦、按需加载和灵活扩展,适合中大型项目的路由管理需求。

  • 核心路由:存放全局基础路由(如登录页、注册页、忘记密码页、404 错误页等),无需权限控制,游客可见。
  • 静态业务路由:(首页、个人中心等所有登录用户基本可见的功能)基础业务路由,需要登录权限但权限要求较低。
  • 动态业务路由:根据角色区分的高级业务路由,权限要求较高,不同角色可见范围不同(权限管理)在用户登录后通过权限接口返回的权限信息筛选 / 生成,再动态添加到路由系统。

目录

路由结构

plaintext

bash 复制代码
src/router/
├── index.ts               # 路由入口(创建实例、注册路由、路由守卫)
├── coreRoutes.ts          # 核心路由定义(登录、404等)
├── staticRoutes.ts        # 静态业务路由定义(首页等基础功能)
├── dynamicRoutes.ts       # 动态路由处理逻辑(后端返回路由)
└── 路由系统说明文档.md              # 路由系统说明文档

路由类型

核心路由 (Core Routes)

  • 定义:无需权限控制的全局基础路由,保障系统最基本功能

  • 特点:项目初始化时直接注册,所有用户(包括未登录用户)可见

  • 包含内容:登录页、注册页、忘记密码页、404 错误页等

  • 示例

    typescript

    typescript 复制代码
    // coreRoutes.ts
    import type { RouteRecordRaw } from 'vue-router';
    
    export const coreRoutes: RouteRecordRaw[] = [
      {
        path: '/login1',
        name: 'Login1',
        component: () => import('@/views/login/LoginView.vue'),
        meta: {
          title: '登录',
          public: true, // 标记为公开路由
          hideHeaderMenus: true
        }
      },
      {
        path: '/404',
        name: 'NotFound',
        component: () => import('@/views/404.vue'),
        meta: { title: '404', public: true }
      },
      {
        path: '/:pathMatch(.*)',
        redirect: '/404',
        meta: { public: true }
      }
    ];

静态业务路由 (Static Routes)

  • 定义:基础业务路由,需要登录权限但权限要求较低

  • 特点:代码中固定定义,登录后经权限筛选动态注册

  • 包含内容:首页、个人中心等所有登录用户基本可见的功能

  • 权限控制 :通过 meta.permission 标识所需权限,无标识则默认登录即可访问

  • 示例

    typescript

    typescript 复制代码
    // staticRoutes.ts
    import type { RouteRecordRaw } from 'vue-router';
    
    export const staticRoutes: RouteRecordRaw[] = [
      {
        path: '/',
        name: 'HomePage',
        component: () => import('@/views/Home/HomePage.vue'),
        meta: {
          title: '首页',
          permission: 'basic' // 需具备basic权限
        }
      },
      {
        path: '/profile',
        name: 'Profile',
        component: () => import('@/views/Profile.vue'),
        meta: { title: '个人中心' } // 无权限标识,登录即可访问
      }
    ];

动态业务路由 (Dynamic Routes)

  • 定义:高级业务路由,权限要求较高,不同角色可见范围不同

  • 特点:由后端接口根据用户权限动态返回,前端处理后注册

  • 包含内容:权限管理、数据报表等角色专属功能页面

  • 处理流程

    1. 登录后调用接口获取动态路由配置
    2. 转换为 Vue Router 兼容格式
    3. 动态注册到路由实例
  • 示例处理函数

    typescript

    typescript 复制代码
    // dynamicRoutes.ts
    import type { RouteRecordRaw } from 'vue-router';
    import axios from 'axios';
    
    // 后端路由数据结构接口
    interface BackendRoute {
      path: string;
      name: string;
      componentPath: string; // 组件路径(如 "permission/RoleMgt")
      meta?: {
        title: string;
        permission?: string;
      };
      children?: BackendRoute[];
    }
    
    // 从后端获取动态路由
    export const fetchDynamicRoutes = async (): Promise<BackendRoute[]> => {
      const response = await axios.get('/api/routes');
      return response.data;
    };
    
    // 转换后端路由为前端可用格式
    export const transformBackendRoutes = (routes: BackendRoute[]): RouteRecordRaw[] => {
      return routes.map(route => {
        const transformed: RouteRecordRaw = {
          path: route.path,
          name: route.name,
          component: () => import(`@/views/${route.componentPath}.vue`),
          meta: route.meta || {},
          children: [] // 初始化children避免undefined
        };
    
        // 递归处理子路由
        if (route.children && route.children.length > 0) {
          transformed.children = transformBackendRoutes(route.children);
        }
    
        return transformed;
      });
    };

权限控制

  1. 权限标识机制

    • 路由通过 meta.permission 定义所需权限(如 permission: 'admin'
    • 公开路由通过 meta.public: true 标识,无需登录即可访问
    • 无权限标识的路由默认需要登录权限
  2. 权限存储

    • 用户权限列表存储在 Pinia 的 permissionStore.menus
    • 权限信息通过 setPermissionsetMenus 方法初始化
    • 权限数据通过 pinia-plugin-persistedstate 持久化到 localStorage
  3. 筛选逻辑

    typescript

    kotlin 复制代码
    // 权限筛选核心函数
    const filterRoutesByPermission = (routes: RouteRecordRaw[]): RouteRecordRaw[] => {
      const { menus } = piniaStore.usePermissionStore;
      return routes.filter(route => {
        // 公开路由直接通过
        if (route.meta?.public) return true;
        // 无权限标识的路由默认需要登录(已在路由守卫中控制)
        if (!route.meta?.permission) return true;
        // 有权限标识的路由需要用户拥有对应权限
        return menus.includes(route.meta.permission);
      });
    };

路由注册流程

  1. 初始化阶段:创建路由实例时仅注册核心路由

    typescript

    arduino 复制代码
    export const router = createRouter({
      history: createWebHashHistory(),
      routes: coreRoutes // 仅注册核心路由
    });
  2. 登录验证阶段:路由守卫检查用户登录状态和权限

  3. 业务路由处理阶段

    • 筛选静态路由:filterRoutesByPermission(staticRoutes)
    • 获取动态路由:fetchDynamicRoutes()
    • 转换动态路由:transformBackendRoutes(rawRoutes)
  4. 动态注册阶段:将处理后的路由添加到路由实例

    typescript

    ini 复制代码
    // 动态添加路由
    allBusinessRoutes.forEach(route => {
      router.addRoute(route);
    });
  5. 完成标识 :设置 routesLoaded: true 避免重复加载

菜单配置

  • 菜单定义在 sideMenu.ts 中,与路由配置一一对应

  • 菜单通过 pathname 与路由关联,确保导航正确性

  • 菜单支持多级嵌套,与路由的 children 结构对应

  • 菜单自动根据路由权限筛选,无权限的菜单自动隐藏

  • 示例

    typescript

    yaml 复制代码
    // sideMenu.ts
    export const menus = [
      {
        path: '/',
        name: 'HomePage',
        meta: {
          title: '首页',
          icon: 'home' // 菜单图标
        }
      },
      {
        path: '/permissionMgt',
        name: 'PermissionMgt',
        meta: {
          title: '权限管理',
          icon: 'permission'
        },
        children: [
          {
            path: '/permissionMgt/roleMgt',
            name: 'RoleMgt',
            meta: { title: '角色管理' }
          },
          {
            path: '/permissionMgt/menuMgt',
            name: 'MenuMgt',
            meta: { title: '菜单管理' }
          }
        ]
      }
    ];

路由守卫

路由守卫 router.beforeEach 实现以下核心功能:

  1. 版本检查:检查系统版本更新,提示用户刷新

  2. 白名单控制:公开路由直接放行,无需登录验证

  3. 登录验证:未登录用户自动跳转至登录页

  4. 路由加载控制:确保业务路由加载完成后再导航

  5. 核心逻辑

    typescript

    scss 复制代码
    router.beforeEach(async (to, from, next) => {
      // 版本检查
      await versionCheck(to);
      
      // 核心路由直接放行
      if (isCoreRoute(to)) {
        next();
        return;
      }
    
      // 未登录用户跳转至登录页
      if (!db.Localdb.get('token')) {
        next('/login1');
        return;
      }
    
      // 初始化业务路由(首次加载时)
      if (!piniaStore.usePermissionStore.routesLoaded) {
        await initBusinessRoutes();
        return next({ ...to, replace: true });
      }
    
      // 正常访问
      next();
    });

开发指南

新增核心路由

  1. coreRoutes.ts 中添加路由配置
  2. 设置 meta.public: true 标识为公开路由
  3. 如需隐藏头部菜单,设置 meta.hideHeaderMenus: true

新增静态业务路由

  1. staticRoutes.ts 中添加路由配置
  2. 根据权限需求设置 meta.permission
  3. sideMenu.ts 中添加对应的菜单配置
  4. 使用懒加载格式:component: () => import('@/views/xxx.vue')

处理动态业务路由

  1. 协调后端确定动态路由接口格式
  2. dynamicRoutes.ts 中扩展 fetchDynamicRoutes 函数
  3. 根据后端格式调整 transformBackendRoutes 转换函数
  4. 确保组件路径与后端返回的 componentPath 匹配

常见问题

  1. 路由不生效

    • 检查路由路径是否正确,是否存在拼写错误
    • 确认路由已添加到对应路由数组
    • 检查浏览器控制台是否有组件加载错误
  2. 权限问题

    • 确认用户权限 permissionStore.menus 已正确初始化
    • 检查路由 meta.permission 配置是否与用户权限匹配
    • 公开路由需设置 meta.public: true
  3. 类型错误

    • 路由类型需使用 import type { RouteRecordRaw } from 'vue-router'
    • 确保 @types/node 已安装,解决 process 未定义问题
    • 自定义全局变量(如 __APP_VERSION__)需在 vite-env.d.ts 中声明
  4. 动态路由加载失败

    • 检查网络请求是否成功获取动态路由

    • 确认 transformBackendRoutes 函数正确转换路由格式

    • 检查动态路由组件路径是否正确

通过以上路由系统设计,实现了权限与路由的解耦、按需加载和灵活扩展,适合中大型项目的路由管理需求。

相关推荐
neon120412 分钟前
Vue 3 父子组件通信核心机制详解:defineProps、defineEmits 与 defineExpose 完全指南
前端·javascript·vue.js·前端框架
Ciito18 分钟前
vue+moment将分钟调整为5的倍数(向下取整)
javascript·vue.js
一点一木1 小时前
Vue Vapor 事件机制深潜:从设计动机到源码解析
前端·vue.js·vapor
小高0072 小时前
🔥10 个被忽视的 Vue3 API 开发利器,用过 5 个才算真正入门
前端·javascript·vue.js
徐小夕2 小时前
开源办公神器OfficeHub:文档、表格、AI 于一体,还能搭知识库!
前端·vue.js·github
mini_0553 小时前
vue3,使用v-draggable拖动时卡顿的问题
前端·javascript·vue.js
CAD老兵3 小时前
前端直接打开 AutoCAD DWG/DXF 文件的 Vue 3 组件来了
前端·javascript·vue.js
new出一个对象3 小时前
vue3使用leaflet地图
vue.js
KingRumn3 小时前
python+vue扫盲
vue.js·python