告别静态路由!vue-pure-admin动态菜单渲染方案:从后端接口到前端页面的全流程解析

告别静态路由!vue-pure-admin动态菜单渲染方案:从后端接口到前端页面的全流程解析

【免费下载链接】vue-pure-admin 【免费下载链接】vue-pure-admin 项目地址: https://gitcode.com/gh_mirrors/vue/vue-pure-admin

在后台管理系统开发中,如何根据用户角色动态展示菜单和路由,是每个开发者都会遇到的痛点。传统静态路由配置不仅维护困难,还无法满足多角色权限控制需求。本文将以vue-pure-admin为例,详细介绍如何基于后端接口实现权限动态路由生成,让你彻底摆脱手动配置路由的烦恼。

动态路由生成的核心价值

动态路由生成是指根据后端返回的权限数据,在前端动态构建路由配置并渲染菜单。这种方式相比静态路由有三个显著优势:

  1. 权限精细化控制:不同角色看到不同菜单,安全性更高
  2. 系统灵活性提升:菜单调整无需前端发版,后端配置即可生效
  3. 维护成本降低:路由配置集中管理,避免前端代码中散落大量路由定义

vue-pure-admin通过src/router/index.tssrc/store/modules/permission.ts两个核心文件实现了完整的动态路由解决方案。

动态路由实现的技术架构

vue-pure-admin的动态路由系统采用"后端返回路由数据+前端处理渲染"的经典架构,整体流程如下:

mermaid

核心模块分工

从后端接口到路由生成的完整流程

1. 后端路由数据格式

vue-pure-admin的后端接口返回的路由数据结构清晰,包含路径、组件、权限等关键信息。以下是mock/asyncRoutes.ts中定义的示例数据:

{
  path: "/system",
  meta: {
    icon: "ri:settings-3-line",
    title: "menus.pureSysManagement",
    rank: system
  },
  children: [
    {
      path: "/system/user/index",
      name: "SystemUser",
      meta: {
        icon: "ri:admin-line",
        title: "menus.pureUser",
        roles: ["admin"] // 仅管理员可见
      }
    },
    // 更多子路由...
  ]
}

每个路由对象包含:

  • path:路由路径
  • name:路由唯一标识
  • meta:路由元信息(图标、标题、权限等)
  • children:子路由数组

2. 前端路由数据处理

获取后端数据后,前端需要进行一系列处理才能将其转化为可使用的路由配置。这个过程主要在src/router/utils.ts中的addAsyncRoutes函数实现:

function addAsyncRoutes(arrRoutes: Array<RouteRecordRaw>) {
  if (!arrRoutes || !arrRoutes.length) return;
  arrRoutes.forEach((v: RouteRecordRaw) => {
    // 标记为后端返回路由
    v.meta.backstage = true;
    
    // 处理父级路由重定向
    if (v?.children && v.children.length && !v.redirect)
      v.redirect = v.children[0].path;
      
    // 处理组件路径
    if (v.meta?.frameSrc) {
      v.component = IFrame; // iframe页面
    } else {
      // 根据路径匹配组件
      const index = modulesRoutesKeys.findIndex(ev => ev.includes(v.path));
      v.component = modulesRoutes[modulesRoutesKeys[index]];
    }
    
    // 递归处理子路由
    if (v?.children && v.children.length) {
      addAsyncRoutes(v.children);
    }
  });
  return arrRoutes;
}

这段代码完成了三项关键工作:

  • 为路由添加后端标识(backstage: true)
  • 处理特殊页面类型(如iframe)
  • 递归处理嵌套路由
  • 匹配前端组件文件

3. 权限过滤与菜单生成

路由数据处理完成后,需要根据用户角色过滤出有权限访问的路由。src/router/utils.ts中的filterNoPermissionTree函数实现了这一功能:

function filterNoPermissionTree(data: RouteComponent[]) {
  const currentRoles = storageLocal().getItem(userKey)?.roles ?? [];
  const newTree = cloneDeep(data).filter((v: any) => 
    isOneOfArray(v.meta?.roles, currentRoles)
  );
  newTree.forEach(
    (v: any) => v.children && (v.children = filterNoPermissionTree(v.children))
  );
  return filterChildrenTree(newTree);
}

该函数通过递归过滤,只保留当前用户角色有权访问的路由。过滤后的路由数据会同时用于:

  • 添加到Vue Router实例实现页面跳转
  • 生成侧边栏菜单供用户导航

路由权限控制的关键实现

基于角色的访问控制(RBAC)

vue-pure-admin采用RBAC权限模型,通过路由元信息中的roles字段控制访问权限。在mock/asyncRoutes.ts中可以看到两种权限控制方式:

// 仅管理员可见
{
  path: "/system/user/index",
  name: "SystemUser",
  meta: {
    title: "menus.pureUser",
    roles: ["admin"] // 只有admin角色可访问
  }
},

// 管理员和普通用户都可见
{
  path: "/permission/page/index",
  name: "PermissionPage",
  meta: {
    title: "menus.purePermissionPage",
    roles: ["admin", "common"] // 多个角色可访问
  }
}

路由守卫中的权限检查

src/router/index.ts的全局前置守卫中,会对即将访问的路由进行权限检查:

router.beforeEach((to, _from, next) => {
  // ...其他逻辑
  if (Cookies.get(multipleTabsKey) && userInfo) {
    // 无权限跳转403页面
    if (to.meta?.roles && !isOneOfArray(to.meta?.roles, userInfo?.roles)) {
      next({ path: "/error/403" });
    }
    // ...其他逻辑
  }
  // ...其他逻辑
});

当用户访问无权限页面时,会被自动重定向到403错误页面。

动态路由的缓存与刷新策略

为提升用户体验,vue-pure-admin实现了路由缓存机制,避免页面频繁加载。核心实现位于src/store/modules/permission.ts

actions: {
  cacheOperate({ mode, name }: cacheType) {
    const delIndex = this.cachePageList.findIndex(v => v === name);
    switch (mode) {
      case "refresh":
        this.cachePageList = this.cachePageList.filter(v => v !== name);
        break;
      case "add":
        this.cachePageList.push(name);
        break;
      case "delete":
        delIndex !== -1 && this.cachePageList.splice(delIndex, 1);
        break;
    }
    // ...缓存优化逻辑
  }
}

同时,系统还处理了刷新页面时的路由恢复问题。在src/router/index.ts的beforeEach钩子中:

if (
  usePermissionStoreHook().wholeMenus.length === 0 &&
  to.path !== "/login"
) {
  initRouter().then((router: Router) => {
    // 刷新后恢复路由状态
    if (isAllEmpty(to.name)) router.push(to.fullPath);
  });
}

当页面刷新且路由数据为空时,会重新初始化路由,确保用户体验不受影响。

实际应用中的最佳实践

1. 路由数据本地缓存

当系统开启路由缓存功能时,vue-pure-admin会将后端返回的路由数据缓存到localStorage中,避免重复请求。相关逻辑在src/router/utils.ts的initRouter函数中:

function initRouter() {
  if (getConfig()?.CachingAsyncRoutes) {
    const key = "async-routes";
    const asyncRouteList = storageLocal().getItem(key) as any;
    if (asyncRouteList && asyncRouteList?.length > 0) {
      return new Promise(resolve => {
        handleAsyncRoutes(asyncRouteList);
        resolve(router);
      });
    } else {
      // 请求后端接口获取路由
      // ...
    }
  }
  // ...
}

2. 路由组件懒加载

为提升性能,vue-pure-admin对路由组件采用了懒加载策略。通过Vite的glob导入功能实现:

// 动态导入所有视图组件
const modulesRoutes = import.meta.glob("/src/views/**/*.{vue,tsx}");

// 匹配路由对应的组件
const index = modulesRoutesKeys.findIndex(ev => ev.includes(v.path));
v.component = modulesRoutes[modulesRoutesKeys[index]];

这种方式不仅减少了初始加载时间,还实现了组件的按需加载。

常见问题与解决方案

1. 路由权限不生效

如果发现动态路由权限控制不生效,可按以下步骤排查:

  1. 检查src/store/modules/permission.ts中的wholeMenus状态是否正确
  2. 确认用户角色是否正确存储在localStorage中
  3. 验证src/router/utils.ts中的isOneOfArray函数是否正常工作

2. 菜单不显示或显示异常

菜单显示异常通常与路由数据处理有关,可重点检查:

  1. src/router/utils.ts中的filterTree函数是否正确过滤了showLink: false的路由
  2. 路由元信息中的icon和title是否正确设置
  3. 路由数据的层级结构是否符合要求

总结与扩展建议

vue-pure-admin的动态路由系统通过清晰的架构设计和完善的功能实现,为后台管理系统提供了灵活高效的权限路由解决方案。核心优势在于:

  1. 完整的权限控制:从路由到按钮的多级权限控制
  2. 高效的路由处理:丰富的工具函数简化路由操作
  3. 优化的用户体验:路由缓存和状态保持提升使用流畅度

对于需要进一步扩展的开发者,建议关注:

  • 实现路由数据的版本控制,支持灰度发布
  • 添加路由操作日志,方便问题排查
  • 开发路由配置可视化界面,降低后端配置难度

通过本文的介绍,相信你已经掌握了vue-pure-admin动态路由生成的核心原理和实现方式。这套方案不仅适用于vue-pure-admin,也可以作为其他Vue后台项目实现动态路由的参考。

需要获取完整代码实现,可以查看项目中的相关文件,或通过以下命令克隆仓库:

git clone https://gitcode.com/gh_mirrors/vue/vue-pure-admin

【免费下载链接】vue-pure-admin 【免费下载链接】vue-pure-admin 项目地址: https://gitcode.com/gh_mirrors/vue/vue-pure-admin

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值