# 路由与导航 **本文引用的文件** - [frontend/src/router/index.js](file://frontend/src/router/index.js) - [frontend/src/main.js](file://frontend/src/main.js) - [frontend/src/App.vue](file://frontend/src/App.vue) - [frontend/src/views/Layout.vue](file://frontend/src/views/Layout.vue) - [frontend/src/views/Login.vue](file://frontend/src/views/Login.vue) - [frontend/src/stores/user.js](file://frontend/src/stores/user.js) - [frontend/src/stores/app.js](file://frontend/src/stores/app.js) - [frontend/src/api/menu.js](file://frontend/src/api/menu.js) - [frontend/src/api/request.js](file://frontend/src/api/request.js) - [frontend/src/views/assessment/AssessmentDetail.vue](file://frontend/src/views/assessment/AssessmentDetail.vue) - [frontend/package.json](file://frontend/package.json) ## 目录 1. [简介](#简介) 2. [项目结构](#项目结构) 3. [核心组件](#核心组件) 4. [架构总览](#架构总览) 5. [详细组件分析](#详细组件分析) 6. [依赖关系分析](#依赖关系分析) 7. [性能考虑](#性能考虑) 8. [故障排查指南](#故障排查指南) 9. [结论](#结论) 10. [附录](#附录) ## 简介 本文件系统化梳理了 Vue Router 在本项目中的路由管理实践,覆盖路由配置、嵌套路由、动态路由、路由守卫与权限拦截、参数传递与查询字符串处理、路由元信息、面包屑与侧边栏联动、页面标题动态更新、懒加载与代码分割、登录态保持与权限控制、以及页面缓存策略等主题。旨在帮助开发者快速理解并扩展路由体系。 ## 项目结构 前端采用 Vite + Vue 3 + vue-router 4 + Pinia 的现代前端栈,路由集中于 router/index.js,视图组件位于 views 下,状态管理位于 stores,HTTP 请求封装于 api/request.js 并通过 axios 实现拦截器。 ```mermaid graph TB A["入口 main.js"] --> B["应用 App.vue"] A --> C["路由 router/index.js"] C --> D["布局 Layout.vue"] D --> E["侧边栏菜单
动态加载"] D --> F["面包屑
基于 meta.title"] D --> G["主内容区 router-view"] G --> H["各业务视图
如 Login.vue / Dashboard.vue / AssessmentDetail.vue"] A --> I["状态 stores
user.js / app.js"] A --> J["HTTP 封装 api/request.js"] ``` 图表来源 - [frontend/src/main.js](file://frontend/src/main.js#L1-L24) - [frontend/src/App.vue](file://frontend/src/App.vue#L1-L17) - [frontend/src/router/index.js](file://frontend/src/router/index.js#L1-L116) - [frontend/src/views/Layout.vue](file://frontend/src/views/Layout.vue#L1-L241) - [frontend/src/views/Login.vue](file://frontend/src/views/Login.vue#L1-L155) - [frontend/src/stores/user.js](file://frontend/src/stores/user.js#L1-L49) - [frontend/src/stores/app.js](file://frontend/src/stores/app.js#L1-L31) - [frontend/src/api/request.js](file://frontend/src/api/request.js#L1-L66) 章节来源 - [frontend/src/main.js](file://frontend/src/main.js#L1-L24) - [frontend/src/router/index.js](file://frontend/src/router/index.js#L1-L116) ## 核心组件 - 路由定义与守卫:集中于 router/index.js,包含基础路由、嵌套路由、动态路由、全局前置守卫。 - 应用入口与挂载:main.js 完成插件注册、路由挂载与国际化配置。 - 布局与导航:Layout.vue 提供侧边栏菜单、面包屑、头部用户信息与内容区过渡动画。 - 登录与权限:Login.vue 负责登录交互;user.js 管理 token 与登录态;全局守卫进行未登录拦截。 - 状态管理:app.js 管理侧边栏折叠与部门树;user.js 管理用户信息与登出跳转。 - HTTP 封装:request.js 统一添加 Authorization 头、处理 401/403/404/500 等响应错误并重定向到登录。 章节来源 - [frontend/src/router/index.js](file://frontend/src/router/index.js#L1-L116) - [frontend/src/main.js](file://frontend/src/main.js#L1-L24) - [frontend/src/views/Layout.vue](file://frontend/src/views/Layout.vue#L1-L241) - [frontend/src/views/Login.vue](file://frontend/src/views/Login.vue#L1-L155) - [frontend/src/stores/user.js](file://frontend/src/stores/user.js#L1-L49) - [frontend/src/stores/app.js](file://frontend/src/stores/app.js#L1-L31) - [frontend/src/api/request.js](file://frontend/src/api/request.js#L1-L66) ## 架构总览 路由层负责页面级导航与权限控制;布局层负责 UI 结构与导航元素;状态层负责登录态与侧边栏状态;HTTP 层统一处理鉴权头与错误响应。 ```mermaid sequenceDiagram participant U as "用户" participant R as "路由守卫
router.beforeEach" participant L as "登录页
Login.vue" participant S as "状态 stores
user.js" participant M as "主布局
Layout.vue" U->>R : 访问任意受保护路由 R->>R : 读取 localStorage.token alt 未登录且非 /login R-->>U : 重定向至 /login else 已登录或访问 /login R-->>M : 放行并渲染布局 end U->>L : 输入凭据并提交 L->>S : 调用 userStore.login(...) S-->>L : 成功则写入 token 并跳转 / L-->>M : 渲染主布局 ``` 图表来源 - [frontend/src/router/index.js](file://frontend/src/router/index.js#L103-L113) - [frontend/src/views/Login.vue](file://frontend/src/views/Login.vue#L73-L89) - [frontend/src/stores/user.js](file://frontend/src/stores/user.js#L11-L20) - [frontend/src/views/Layout.vue](file://frontend/src/views/Layout.vue#L1-L241) ## 详细组件分析 ### 路由配置与嵌套路由 - 基础路由:登录页与根路径重定向。 - 嵌套路由:根路径下包含 dashboard、基础数据、考核、工资、报表、财务、计划、系统管理等模块;系统管理内部再嵌套菜单管理。 - 路由元信息:每个路由配置了 meta.title、icon 等,用于面包屑与侧边栏展示。 - 默认重定向:访问根路径时自动跳转到 dashboard。 章节来源 - [frontend/src/router/index.js](file://frontend/src/router/index.js#L3-L96) ### 动态路由与菜单联动 - 后端菜单树:通过 api/menu.js 的 getMenuTree 接口拉取菜单树,转换为前端可用的菜单项数组。 - 侧边栏渲染:Layout.vue 中根据 menuItems 渲染 el-menu,支持折叠与图标显示。 - 回退菜单:若拉取失败,使用默认菜单作为后备,保证可用性。 章节来源 - [frontend/src/views/Layout.vue](file://frontend/src/views/Layout.vue#L86-L124) - [frontend/src/api/menu.js](file://frontend/src/api/menu.js#L4-L6) ### 动态路由参数与详情页 - 动态路由:/assessments/:id 对应 AssessmentDetail 页面。 - 参数读取:在 AssessmentDetail.vue 中通过 useRoute().params.id 获取动态参数。 - 查询字符串:当前示例未使用查询参数,但可通过 useRoute().query 获取。 章节来源 - [frontend/src/router/index.js](file://frontend/src/router/index.js#L52-L56) - [frontend/src/views/assessment/AssessmentDetail.vue](file://frontend/src/views/assessment/AssessmentDetail.vue#L104-L158) ### 路由元信息与面包屑 - 元信息:每个路由的 meta.title 用于页面标题与面包屑显示。 - 面包屑:Layout.vue 中直接使用 route.meta.title 作为当前面包屑文本。 - 页面标题:全局守卫在每次导航前设置 document.title。 章节来源 - [frontend/src/router/index.js](file://frontend/src/router/index.js#L8-L20) - [frontend/src/router/index.js](file://frontend/src/router/index.js#L104-L106) - [frontend/src/views/Layout.vue](file://frontend/src/views/Layout.vue#L39-L42) ### 路由守卫、权限验证与导航拦截 - 全局前置守卫:router.beforeEach 读取 token,未登录且访问非 /login 路由时强制跳转登录。 - 登录成功:Login.vue 调用 userStore.login,成功后写入 token 并跳转根路径。 - 登出:user.js 中 logout 清空 token 并跳转 /login。 - HTTP 层拦截:request.js 在 401 时清除 token 并跳转 /login,确保前后端一致。 ```mermaid flowchart TD Start(["进入 beforeEach"]) --> ReadToken["读取 localStorage.token"] ReadToken --> IsLogin{"是否已登录?"} IsLogin --> |是| Next["放行 next()"] IsLogin --> |否| IsLoginPage{"是否访问 /login?"} IsLoginPage --> |是| Next IsLoginPage --> |否| ToLogin["next('/login')"] Next --> End(["结束"]) ToLogin --> End ``` 图表来源 - [frontend/src/router/index.js](file://frontend/src/router/index.js#L103-L113) 章节来源 - [frontend/src/router/index.js](file://frontend/src/router/index.js#L103-L113) - [frontend/src/views/Login.vue](file://frontend/src/views/Login.vue#L73-L89) - [frontend/src/stores/user.js](file://frontend/src/stores/user.js#L34-L39) - [frontend/src/api/request.js](file://frontend/src/api/request.js#L39-L44) ### 页面标题动态更新 - 标题来源:全局守卫根据 to.meta.title 设置 document.title,并附加系统名。 - 建议:可在路由 meta 中补充更完整的标题链路,便于多级标题展示。 章节来源 - [frontend/src/router/index.js](file://frontend/src/router/index.js#L104-L106) ### 面包屑导航与侧边栏菜单 - 面包屑:Layout.vue 使用 route.meta.title 显示当前页面标题。 - 侧边栏:Layout.vue 通过 api/menu.js 拉取菜单树,渲染 el-menu;支持折叠与图标。 - 菜单回退:若接口异常,使用默认菜单保障可用性。 章节来源 - [frontend/src/views/Layout.vue](file://frontend/src/views/Layout.vue#L39-L42) - [frontend/src/views/Layout.vue](file://frontend/src/views/Layout.vue#L86-L124) - [frontend/src/api/menu.js](file://frontend/src/api/menu.js#L4-L6) ### 页面缓存机制 - 当前实现:Layout.vue 使用 包裹并配合过渡动画,未见显式 keep-alive 缓存策略。 - 建议:对频繁切换但无需刷新的页面(如列表页)可引入 keep-alive 以提升体验。 章节来源 - [frontend/src/views/Layout.vue](file://frontend/src/views/Layout.vue#L63-L67) ### 登录状态保持与权限控制 - 登录态:token 存储于 localStorage,user.js 提供 login/getUserInfo/logout。 - 权限控制:全局守卫 + HTTP 401 自动登出,确保未授权访问被拦截。 - 用户信息:userStore.getUserInfo 用于初始化用户信息,但当前布局未消费该数据。 章节来源 - [frontend/src/stores/user.js](file://frontend/src/stores/user.js#L7-L31) - [frontend/src/api/request.js](file://frontend/src/api/request.js#L39-L44) ### 路由懒加载与代码分割 - 路由级懒加载:各路由 component 使用函数返回动态 import,实现按需加载与代码分割。 - 性能收益:首屏仅加载必要模块,减少初始包体积。 章节来源 - [frontend/src/router/index.js](file://frontend/src/router/index.js#L7-L8) - [frontend/src/router/index.js](file://frontend/src/router/index.js#L18) - [frontend/src/router/index.js](file://frontend/src/router/index.js#L24) - [frontend/src/router/index.js](file://frontend/src/router/index.js#L30) - [frontend/src/router/index.js](file://frontend/src/router/index.js#L36) - [frontend/src/router/index.js](file://frontend/src/router/index.js#L42) - [frontend/src/router/index.js](file://frontend/src/router/index.js#L48) - [frontend/src/router/index.js](file://frontend/src/router/index.js#L54) - [frontend/src/router/index.js](file://frontend/src/router/index.js#L60) - [frontend/src/router/index.js](file://frontend/src/router/index.js#L66) - [frontend/src/router/index.js](file://frontend/src/router/index.js#L72) - [frontend/src/router/index.js](file://frontend/src/router/index.js#L78) - [frontend/src/router/index.js](file://frontend/src/router/index.js#L89) ### 查询字符串处理 - 当前路由未使用查询参数示例;如需使用,可在组件中通过 useRoute().query 获取。 - 建议:对需要持久化的筛选条件,优先使用查询参数,利于分享与刷新恢复。 章节来源 - [frontend/src/views/assessment/AssessmentDetail.vue](file://frontend/src/views/assessment/AssessmentDetail.vue#L104-L158) ## 依赖关系分析 - 路由依赖:router/index.js 依赖 Vue Router 与本地组件;全局守卫依赖 localStorage。 - 视图依赖:Layout.vue 依赖 stores 与 api/menu.js;Login.vue 依赖 stores/user.js 与 router。 - 状态依赖:user.js 依赖 api/auth 与 router;app.js 依赖 api/department。 - HTTP 依赖:api/request.js 依赖 axios,并向 router 注入 401 自动登出逻辑。 ```mermaid graph LR R["router/index.js"] --> V1["views/Login.vue"] R --> V2["views/Layout.vue"] V2 --> S1["stores/user.js"] V2 --> S2["stores/app.js"] V2 --> A1["api/menu.js"] V1 --> S1 S1 --> A2["api/auth (未在仓库中)"] A1 --> A3["后端 /menus* 接口"] R --> R2["全局守卫使用 localStorage"] R --> A4["api/request.js"] ``` 图表来源 - [frontend/src/router/index.js](file://frontend/src/router/index.js#L1-L116) - [frontend/src/views/Layout.vue](file://frontend/src/views/Layout.vue#L1-L241) - [frontend/src/views/Login.vue](file://frontend/src/views/Login.vue#L1-L155) - [frontend/src/stores/user.js](file://frontend/src/stores/user.js#L1-L49) - [frontend/src/stores/app.js](file://frontend/src/stores/app.js#L1-L31) - [frontend/src/api/menu.js](file://frontend/src/api/menu.js#L1-L37) - [frontend/src/api/request.js](file://frontend/src/api/request.js#L1-L66) 章节来源 - [frontend/src/router/index.js](file://frontend/src/router/index.js#L1-L116) - [frontend/src/views/Layout.vue](file://frontend/src/views/Layout.vue#L1-L241) - [frontend/src/views/Login.vue](file://frontend/src/views/Login.vue#L1-L155) - [frontend/src/stores/user.js](file://frontend/src/stores/user.js#L1-L49) - [frontend/src/stores/app.js](file://frontend/src/stores/app.js#L1-L31) - [frontend/src/api/menu.js](file://frontend/src/api/menu.js#L1-L37) - [frontend/src/api/request.js](file://frontend/src/api/request.js#L1-L66) ## 性能考虑 - 路由懒加载:已通过动态 import 实现按需加载,建议结合路由级命名与路由表拆分进一步优化打包。 - 过渡动画:Layout.vue 已启用淡入淡出过渡,避免白屏与闪烁。 - 首屏优化:将高频但非首屏使用的路由组件继续延迟加载,减少首屏 JS 体积。 - 缓存策略:对列表类页面引入 keep-alive,减少重复渲染与请求开销。 - 图标与第三方库:Element Plus 图标已全局注册,避免重复导入。 章节来源 - [frontend/src/router/index.js](file://frontend/src/router/index.js#L7-L8) - [frontend/src/views/Layout.vue](file://frontend/src/views/Layout.vue#L63-L67) - [frontend/package.json](file://frontend/package.json#L11-L25) ## 故障排查指南 - 无法进入受保护页面 - 检查 localStorage 是否存在 token;确认全局守卫逻辑与路由路径匹配。 - 登录后未跳转 - 检查 Login.vue 中调用 userStore.login 返回值与 router.push('/') 执行。 - 401 自动登出后未回到登录页 - 检查 request.js 响应拦截器是否正确清除 token 并跳转 /login。 - 面包屑不显示 - 确认目标路由 meta.title 是否配置;Layout.vue 是否使用 route.meta.title。 - 侧边栏菜单为空 - 检查 api/menu.js 接口返回结构与 Layout.vue 转换逻辑;关注异常回退分支。 章节来源 - [frontend/src/router/index.js](file://frontend/src/router/index.js#L103-L113) - [frontend/src/views/Login.vue](file://frontend/src/views/Login.vue#L73-L89) - [frontend/src/api/request.js](file://frontend/src/api/request.js#L39-L44) - [frontend/src/views/Layout.vue](file://frontend/src/views/Layout.vue#L86-L124) ## 结论 本项目的路由体系以简洁清晰的方式实现了基础导航、嵌套与动态路由、全局守卫与权限拦截、元信息驱动的面包屑与标题、以及基于懒加载的代码分割。后续可在菜单树结构、页面缓存策略、查询参数与面包屑链路等方面进一步增强,以满足更复杂的业务场景与用户体验需求。 ## 附录 - 技术栈版本参考 - Vue 3、Vue Router 4、Pinia、Element Plus、Axios、Vite - 建议的后续改进方向 - 菜单树结构标准化与权限位映射 - 面包屑链路与路由层级联动 - 页面级 keep-alive 缓存策略 - 查询参数与路由参数的规范使用 章节来源 - [frontend/package.json](file://frontend/package.json#L11-L25)