Vue3动态菜单和路由
后台系统里面,菜单和路由一般都不是写死的,而是根据后端返回的权限动态生成,这里记录一下 Vue3 中动态菜单和动态路由的处理方式
后台系统和普通官网不太一样,普通官网路由基本可以写死,但是后台系统通常需要根据账号权限显示不同菜单。
比如管理员可以看到所有菜单,普通账号只能看到部分菜单。
所以这里就会涉及两个问题:
- 左侧菜单怎么显示
- 页面路由怎么注册
后端菜单
一般后端返回的菜单都是树形结构,类似这样:
1 | [ |
前端要做的事情就是把这份菜单转换成侧边栏和路由。
收集页面
因为使用的是 Vite,所以动态收集页面使用的是 import.meta.glob。
1 | const modules = import.meta.glob('/src/views/pages/**/*.vue'); |
这行代码会把 src/views/pages 下面的 .vue 页面全部收集起来。
比如页面文件是:
1 | src/views/pages/demo/table/index.vue |
那么后面就可以通过路径找到这个页面。
路径处理
后端返回的 menuPath 不一定刚好等于页面文件路径,所以这里需要做一层处理。
我这里约定普通页面路径为:
1 | /src/views/pages + menuPath + /index.vue |
比如:
1 | menuPath: /demo/table |
对应页面就是:
1 | /src/views/pages/demo/table/index.vue |
如果菜单路径和真实文件路径不一致,也可以增加一个 componentPath 字段专门指定页面路径。
这样菜单显示路径和页面文件路径就可以分开。
生成路由
菜单本身是树形结构,但是 Vue Router 最终需要的是路由配置。
所以需要递归菜单树,把真正的叶子页面转成路由:
1 | { |
这里有几个点需要注意。
path
path 使用后端返回的 menuPath。
因为这个路径是用户真正访问的地址,也是侧边栏点击后跳转的地址。
name
name 不建议随便写。
我这里优先使用 menuId,因为动态路由后面可能需要删除、重置、判断是否已经存在。
如果 name 不稳定,后面处理会比较麻烦。
meta
meta 里面主要放页面显示需要用到的信息。
比如:
1 | meta: { |
标题、菜单 id、顶级菜单 id 都可以放进去,后面标签页、面包屑、菜单高亮都能用到。
component
动态路由里面最重要的是组件匹配。
大概逻辑是:
1 | const pagePathCandidates = [ |
如果匹配不到页面,就跳到 404 页面,避免整个路由注册失败。
侧边栏菜单
路由需要完整菜单,但是侧边栏不一定要显示全部。
比如按钮权限、隐藏菜单都不应该显示在侧边栏里面。
所以还需要过滤一次:
1 | const filterHidden = (menuList = []) => { |
这样侧边栏只显示需要展示的菜单。
Pinia保存
菜单和权限需要保存到 Pinia 中:
1 | state: () => { |
其中:
menuList保存完整菜单showMenu保存侧边栏菜单apiList保存按钮权限activeIndex保存当前选中菜单
并且需要开启持久化,因为刷新页面的时候还要恢复动态路由。
总结
动态菜单和动态路由的核心其实就三步:
- 后端返回菜单树
- 前端过滤出侧边栏菜单
- 前端把菜单转换成 Vue Router 路由
这里面比较容易踩坑的是页面路径匹配和刷新后路由丢失。
路径匹配可以通过 componentPath 兜底,刷新恢复下一篇再单独记录。
以上就是我对 Vue3 动态菜单和路由的一些理解,如有错误,欢迎大佬指出。