16 KiB
16 KiB
路由与导航
**本文引用的文件** - [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)目录
简介
本文件系统化梳理了 Vue Router 在本项目中的路由管理实践,覆盖路由配置、嵌套路由、动态路由、路由守卫与权限拦截、参数传递与查询字符串处理、路由元信息、面包屑与侧边栏联动、页面标题动态更新、懒加载与代码分割、登录态保持与权限控制、以及页面缓存策略等主题。旨在帮助开发者快速理解并扩展路由体系。
项目结构
前端采用 Vite + Vue 3 + vue-router 4 + Pinia 的现代前端栈,路由集中于 router/index.js,视图组件位于 views 下,状态管理位于 stores,HTTP 请求封装于 api/request.js 并通过 axios 实现拦截器。
graph TB
A["入口 main.js"] --> B["应用 App.vue"]
A --> C["路由 router/index.js"]
C --> D["布局 Layout.vue"]
D --> E["侧边栏菜单<br/>动态加载"]
D --> F["面包屑<br/>基于 meta.title"]
D --> G["主内容区 router-view"]
G --> H["各业务视图<br/>如 Login.vue / Dashboard.vue / AssessmentDetail.vue"]
A --> I["状态 stores<br/>user.js / app.js"]
A --> J["HTTP 封装 api/request.js"]
图表来源
- frontend/src/main.js
- frontend/src/App.vue
- frontend/src/router/index.js
- frontend/src/views/Layout.vue
- frontend/src/views/Login.vue
- frontend/src/stores/user.js
- frontend/src/stores/app.js
- frontend/src/api/request.js
章节来源
核心组件
- 路由定义与守卫:集中于 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
- frontend/src/main.js
- frontend/src/views/Layout.vue
- frontend/src/views/Login.vue
- frontend/src/stores/user.js
- frontend/src/stores/app.js
- frontend/src/api/request.js
架构总览
路由层负责页面级导航与权限控制;布局层负责 UI 结构与导航元素;状态层负责登录态与侧边栏状态;HTTP 层统一处理鉴权头与错误响应。
sequenceDiagram
participant U as "用户"
participant R as "路由守卫<br/>router.beforeEach"
participant L as "登录页<br/>Login.vue"
participant S as "状态 stores<br/>user.js"
participant M as "主布局<br/>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
- frontend/src/views/Login.vue
- frontend/src/stores/user.js
- frontend/src/views/Layout.vue
详细组件分析
路由配置与嵌套路由
- 基础路由:登录页与根路径重定向。
- 嵌套路由:根路径下包含 dashboard、基础数据、考核、工资、报表、财务、计划、系统管理等模块;系统管理内部再嵌套菜单管理。
- 路由元信息:每个路由配置了 meta.title、icon 等,用于面包屑与侧边栏展示。
- 默认重定向:访问根路径时自动跳转到 dashboard。
章节来源
动态路由与菜单联动
- 后端菜单树:通过 api/menu.js 的 getMenuTree 接口拉取菜单树,转换为前端可用的菜单项数组。
- 侧边栏渲染:Layout.vue 中根据 menuItems 渲染 el-menu,支持折叠与图标显示。
- 回退菜单:若拉取失败,使用默认菜单作为后备,保证可用性。
章节来源
动态路由参数与详情页
- 动态路由:/assessments/:id 对应 AssessmentDetail 页面。
- 参数读取:在 AssessmentDetail.vue 中通过 useRoute().params.id 获取动态参数。
- 查询字符串:当前示例未使用查询参数,但可通过 useRoute().query 获取。
章节来源
路由元信息与面包屑
- 元信息:每个路由的 meta.title 用于页面标题与面包屑显示。
- 面包屑:Layout.vue 中直接使用 route.meta.title 作为当前面包屑文本。
- 页面标题:全局守卫在每次导航前设置 document.title。
章节来源
路由守卫、权限验证与导航拦截
- 全局前置守卫:router.beforeEach 读取 token,未登录且访问非 /login 路由时强制跳转登录。
- 登录成功:Login.vue 调用 userStore.login,成功后写入 token 并跳转根路径。
- 登出:user.js 中 logout 清空 token 并跳转 /login。
- HTTP 层拦截:request.js 在 401 时清除 token 并跳转 /login,确保前后端一致。
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
- frontend/src/views/Login.vue
- frontend/src/stores/user.js
- frontend/src/api/request.js
页面标题动态更新
- 标题来源:全局守卫根据 to.meta.title 设置 document.title,并附加系统名。
- 建议:可在路由 meta 中补充更完整的标题链路,便于多级标题展示。
章节来源
面包屑导航与侧边栏菜单
- 面包屑:Layout.vue 使用 route.meta.title 显示当前页面标题。
- 侧边栏:Layout.vue 通过 api/menu.js 拉取菜单树,渲染 el-menu;支持折叠与图标。
- 菜单回退:若接口异常,使用默认菜单保障可用性。
章节来源
页面缓存机制
- 当前实现:Layout.vue 使用 包裹并配合过渡动画,未见显式 keep-alive 缓存策略。
- 建议:对频繁切换但无需刷新的页面(如列表页)可引入 keep-alive 以提升体验。
章节来源
登录状态保持与权限控制
- 登录态:token 存储于 localStorage,user.js 提供 login/getUserInfo/logout。
- 权限控制:全局守卫 + HTTP 401 自动登出,确保未授权访问被拦截。
- 用户信息:userStore.getUserInfo 用于初始化用户信息,但当前布局未消费该数据。
章节来源
路由懒加载与代码分割
- 路由级懒加载:各路由 component 使用函数返回动态 import,实现按需加载与代码分割。
- 性能收益:首屏仅加载必要模块,减少初始包体积。
章节来源
- frontend/src/router/index.js
- frontend/src/router/index.js
- frontend/src/router/index.js
- frontend/src/router/index.js
- frontend/src/router/index.js
- frontend/src/router/index.js
- frontend/src/router/index.js
- frontend/src/router/index.js
- frontend/src/router/index.js
- frontend/src/router/index.js
- frontend/src/router/index.js
- frontend/src/router/index.js
- frontend/src/router/index.js
查询字符串处理
- 当前路由未使用查询参数示例;如需使用,可在组件中通过 useRoute().query 获取。
- 建议:对需要持久化的筛选条件,优先使用查询参数,利于分享与刷新恢复。
章节来源
依赖关系分析
- 路由依赖: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 自动登出逻辑。
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
- frontend/src/views/Layout.vue
- frontend/src/views/Login.vue
- frontend/src/stores/user.js
- frontend/src/stores/app.js
- frontend/src/api/menu.js
- frontend/src/api/request.js
章节来源
- frontend/src/router/index.js
- frontend/src/views/Layout.vue
- frontend/src/views/Login.vue
- frontend/src/stores/user.js
- frontend/src/stores/app.js
- frontend/src/api/menu.js
- frontend/src/api/request.js
性能考虑
- 路由懒加载:已通过动态 import 实现按需加载,建议结合路由级命名与路由表拆分进一步优化打包。
- 过渡动画:Layout.vue 已启用淡入淡出过渡,避免白屏与闪烁。
- 首屏优化:将高频但非首屏使用的路由组件继续延迟加载,减少首屏 JS 体积。
- 缓存策略:对列表类页面引入 keep-alive,减少重复渲染与请求开销。
- 图标与第三方库:Element Plus 图标已全局注册,避免重复导入。
章节来源
故障排查指南
- 无法进入受保护页面
- 检查 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
- frontend/src/views/Login.vue
- frontend/src/api/request.js
- frontend/src/views/Layout.vue
结论
本项目的路由体系以简洁清晰的方式实现了基础导航、嵌套与动态路由、全局守卫与权限拦截、元信息驱动的面包屑与标题、以及基于懒加载的代码分割。后续可在菜单树结构、页面缓存策略、查询参数与面包屑链路等方面进一步增强,以满足更复杂的业务场景与用户体验需求。
附录
- 技术栈版本参考
- Vue 3、Vue Router 4、Pinia、Element Plus、Axios、Vite
- 建议的后续改进方向
- 菜单树结构标准化与权限位映射
- 面包屑链路与路由层级联动
- 页面级 keep-alive 缓存策略
- 查询参数与路由参数的规范使用
章节来源