动态路由
TechUI 的动态路由机制是后台管理系统的核心。它采用 后端控制策略,即路由表并非在前端硬编码,而是根据当前登录用户的权限,由后端返回菜单数据,前端再结合本地的组件注册表,动态生成 Vue Router 路由配置。
工作原理
- 组件注册:前端维护一份
register.js,将路由标识(字符串)映射到真实的 Vue 组件文件。 - 获取菜单:用户登录后,从后端或本地数据库获取菜单树数据。
- 路由匹配:通过
routerPackag函数,将菜单数据中的label与组件注册表匹配。 - 动态挂载:使用
router.addRoute将匹配成功的组件挂载到名为layout的父路由下。 - 全局拦截:在
router.beforeEach中处理页面刷新、鉴权、Tab 页签添加等逻辑。
核心流程解析
动态路由的逻辑主要集中在全局 Provider 组件中,仅当 isActAdminFeatures 为真时激活。
数据持久化与同步
为了防止页面刷新导致 Vuex/Pinia 状态丢失,TechUI 实现了双重持久化机制:
- 用户信息 (UserInfo): 通过
watch监听$tState.ADMIN.userInfo。一旦变化,使用tStoreCrypto(加密存储)同步到 Session/Local Storage。同时,如果发现 Token 或 ID 丢失,会自动触发登出。 - 菜单数据 (Menu): 通过
watch监听$tState.ADMIN.menu。一旦获取到菜单数据,会将其写入 IndexedDB (TuiDB)。这确保了即使用户刷新页面,也能从本地数据库快速恢复庞大的菜单结构,而无需再次请求后端。
路由组装 (routerPackag)
routerPackag 是将业务数据转换为路由配置的核心函数。
javascript
// 伪代码逻辑演示
async function routerPackag(routerData){
for(let i=0; i<routerData.length; i++){
// 1. 解构后端数据
let { label, icon, title, keepAlive, ... } = routerData[i];
// 2. 在注册表中查找组件
let comp = $tState.ADMIN.componentRegister[label];
// 3. 如果不是父节点且组件存在
if(!isParent && comp){
// 4. 动态添加路由到 'layout' 父节点
router.addRoute("layout", {
path: '/' + label, // 默认路径为 /label
name: label, // 路由名称
component: comp, // 对应的 Vue 组件
meta: { ... } // 注入 meta 信息
});
}
}
}关键点:
- 所有动态路由都是
layout路由的子路由。这意味着它们都会渲染在TuiAdminLayout的<router-view>中。 - 路由的
name和path均基于label生成,确保唯一性。
全局路由拦截 (Guard)
router.beforeEach 承担了“守门员”的角色,处理了复杂的初始化逻辑:
阶段一:状态恢复
- 用户恢复:如果状态树中没有 UserID,尝试从本地存储读取 Token 恢复登录态。
- 菜单恢复:如果状态树中没有菜单数据,尝试从 IndexedDB (
TuiDB) 中读取缓存菜单。
阶段二:动态加载
- 初始化检查:检查
$ARouterInited标记。 - 执行加载:如果未初始化,调用
routerPackag($AMenu.value)执行路由挂载。 - 状态翻转:挂载完成后,将
$ADMIN.value.routerInited设为true,并重新触发路由跳转(next({ ...to }))以确保新路由生效。
阶段三:业务处理
- Tab 管理:如果目标路由有效且设置了
label,会自动调用tabAdd(to)将其加入顶部页签栏。 - 缓存管理:如果
meta.keepAlive为真,会自动将其name加入$AKeepAlive列表。 - 鉴权失败:如果上述检查均未通过(无 Token、无 ID),则强制重定向至
/login。
后端数据结构规范
为了配合动态路由,后端返回的菜单数据(或本地 Mock 数据)应符合以下 JSON 结构:
json
[
{
"label": "dashboard", // 核心字段:对应 register.js 中的键名
"title": "仪表盘", // 页面标题
"icon": "ti-dashboard", // 菜单图标
"isParent": false, // 是否为父级菜单目录
"keepAlive": true, // 是否开启页面缓存
"hideInMenu": false, // 是否在侧边栏隐藏
"hideInTab": false, // 是否在 Tab 栏隐藏
"parentId": "root" // 父节点 ID
}
]常见问题
Q: 为什么刷新页面后显示 404?A: 通常是因为动态路由尚未加载完成。TechUI 的 beforeEach 逻辑中处理了这种情况:在刷新时,它会先从 IndexedDB 恢复菜单并重新注册路由,然后再执行跳转。如果出现 404,请检查 IndexedDB 是否成功写入了 menus 数据。
Q: 新增页面如何配置?A: 需要两步:
- 在前端
register.js中引入.vue文件并导出。 - 在后端(或 Mock 数据)中添加对应的菜单项,确保
label与register.js中的键名一致。