路由系统说明文档
概述
按「路由类型拆分解耦」
核心思路:路由系统按职责划分为三类:核心路由、静态业务路由和动态业务路由,分别对应不同的权限需求和加载时机,通过以上路由系统设计,实现了权限与路由的解耦、按需加载和灵活扩展,适合中大型项目的路由管理需求。
- 核心路由:存放全局基础路由(如登录页、注册页、忘记密码页、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)
-
定义:高级业务路由,权限要求较高,不同角色可见范围不同
-
特点:由后端接口根据用户权限动态返回,前端处理后注册
-
包含内容:权限管理、数据报表等角色专属功能页面
-
处理流程:
- 登录后调用接口获取动态路由配置
- 转换为 Vue Router 兼容格式
- 动态注册到路由实例
-
示例处理函数:
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; }); };
权限控制
-
权限标识机制:
- 路由通过
meta.permission
定义所需权限(如permission: 'admin'
) - 公开路由通过
meta.public: true
标识,无需登录即可访问 - 无权限标识的路由默认需要登录权限
- 路由通过
-
权限存储:
- 用户权限列表存储在 Pinia 的
permissionStore.menus
中 - 权限信息通过
setPermission
或setMenus
方法初始化 - 权限数据通过 pinia-plugin-persistedstate 持久化到 localStorage
- 用户权限列表存储在 Pinia 的
-
筛选逻辑:
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); }); };
路由注册流程
-
初始化阶段:创建路由实例时仅注册核心路由
typescript
arduinoexport const router = createRouter({ history: createWebHashHistory(), routes: coreRoutes // 仅注册核心路由 });
-
登录验证阶段:路由守卫检查用户登录状态和权限
-
业务路由处理阶段:
- 筛选静态路由:
filterRoutesByPermission(staticRoutes)
- 获取动态路由:
fetchDynamicRoutes()
- 转换动态路由:
transformBackendRoutes(rawRoutes)
- 筛选静态路由:
-
动态注册阶段:将处理后的路由添加到路由实例
typescript
ini// 动态添加路由 allBusinessRoutes.forEach(route => { router.addRoute(route); });
-
完成标识 :设置
routesLoaded: true
避免重复加载
菜单配置
-
菜单定义在
sideMenu.ts
中,与路由配置一一对应 -
菜单通过
path
或name
与路由关联,确保导航正确性 -
菜单支持多级嵌套,与路由的 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
实现以下核心功能:
-
版本检查:检查系统版本更新,提示用户刷新
-
白名单控制:公开路由直接放行,无需登录验证
-
登录验证:未登录用户自动跳转至登录页
-
路由加载控制:确保业务路由加载完成后再导航
-
核心逻辑:
typescript
scssrouter.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(); });
开发指南
新增核心路由
- 在
coreRoutes.ts
中添加路由配置 - 设置
meta.public: true
标识为公开路由 - 如需隐藏头部菜单,设置
meta.hideHeaderMenus: true
新增静态业务路由
- 在
staticRoutes.ts
中添加路由配置 - 根据权限需求设置
meta.permission
- 在
sideMenu.ts
中添加对应的菜单配置 - 使用懒加载格式:
component: () => import('@/views/xxx.vue')
处理动态业务路由
- 协调后端确定动态路由接口格式
- 在
dynamicRoutes.ts
中扩展fetchDynamicRoutes
函数 - 根据后端格式调整
transformBackendRoutes
转换函数 - 确保组件路径与后端返回的
componentPath
匹配
常见问题
-
路由不生效:
- 检查路由路径是否正确,是否存在拼写错误
- 确认路由已添加到对应路由数组
- 检查浏览器控制台是否有组件加载错误
-
权限问题:
- 确认用户权限
permissionStore.menus
已正确初始化 - 检查路由
meta.permission
配置是否与用户权限匹配 - 公开路由需设置
meta.public: true
- 确认用户权限
-
类型错误:
- 路由类型需使用
import type { RouteRecordRaw } from 'vue-router'
- 确保
@types/node
已安装,解决process
未定义问题 - 自定义全局变量(如
__APP_VERSION__
)需在vite-env.d.ts
中声明
- 路由类型需使用
-
动态路由加载失败:
-
检查网络请求是否成功获取动态路由
-
确认
transformBackendRoutes
函数正确转换路由格式 -
检查动态路由组件路径是否正确
-
通过以上路由系统设计,实现了权限与路由的解耦、按需加载和灵活扩展,适合中大型项目的路由管理需求。