开启掘金成长之旅!这是我参与「掘金日新计划 · 12 月更文挑战」的第5天,点击查看活动详情
什么是动态路由
根据路由表的配置,自动生成对应的 menu 菜单。当路由表发生变化时,menu 菜单自动发生变化
动态路由的实现
要实现的动态路由效果如图
要实现上述效果,需要获取到所有的路由信息,并且得到合理格式的路由表,再去渲染。
想要获取路由表数据,我们可以使用getRoutes方法
<script setup>
import { useRouter } from 'vue-router'
const router = useRouter()
console.log(router.getRoutes())
</script>
得到的数据如下图所示
[
{
"path":"/user/info/:id",
"name":"userInfo",
"meta":{
"title":"userInfo"
},
"children":[
]
},
{
"path":"/article/editor/:id",
"meta":{
"title":"articleEditor"
},
"children":[
]
},
{
"path":"/user/manage",
"meta":{
"title":"userManage",
"icon":"personnel-manage"
},
"children":[
]
},
{
"path":"/user/role",
"meta":{
"title":"roleList",
"icon":"role"
},
"children":[
]
},
{
"path":"/user/permission",
"meta":{
"title":"permissionList",
"icon":"permission"
},
"children":[
]
},
{
"path":"/user/import",
"name":"import",
"meta":{
"title":"excelImport"
},
"children":[
]
},
{
"path":"/article/ranking",
"meta":{
"title":"articleRanking",
"icon":"article-ranking"
},
"children":[
]
},
{
"path":"/article/create",
"meta":{
"title":"articleCreate",
"icon":"article-create"
},
"children":[
]
},
{
"path":"/article/:id",
"meta":{
"title":"articleDetail"
},
"children":[
]
},
{
"path":"/login",
"meta":{
},
"children":[
]
},
{
"path":"/profile",
"name":"profile",
"meta":{
"title":"profile",
"icon":"el-icon-user"
},
"children":[
]
},
{
"path":"/404",
"name":"404",
"meta":{
},
"children":[
]
},
{
"path":"/401",
"name":"401",
"meta":{
},
"children":[
]
},
{
"path":"/",
"redirect":"/profile",
"meta":{
},
"children":[
{
"path":"/profile",
"name":"profile",
"meta":{
"title":"profile",
"icon":"el-icon-user"
}
},
{
"path":"/404",
"name":"404"
},
{
"path":"/401",
"name":"401"
}
]
},
{
"path":"/user",
"redirect":"/user/manage",
"meta":{
"title":"user",
"icon":"personnel"
},
"children":[
{
"path":"/user/manage",
"meta":{
"title":"userManage",
"icon":"personnel-manage"
}
},
{
"path":"/user/role",
"meta":{
"title":"roleList",
"icon":"role"
}
},
{
"path":"/user/permission",
"meta":{
"title":"permissionList",
"icon":"permission"
}
},
{
"path":"/user/info/:id",
"name":"userInfo",
"meta":{
"title":"userInfo"
}
},
{
"path":"/user/import",
"name":"import",
"meta":{
"title":"excelImport"
}
}
]
},
{
"path":"/article",
"redirect":"/article/ranking",
"meta":{
"title":"article",
"icon":"article"
},
"children":[
{
"path":"/article/ranking",
"meta":{
"title":"articleRanking",
"icon":"article-ranking"
}
},
{
"path":"/article/:id",
"meta":{
"title":"articleDetail"
}
},
{
"path":"/article/create",
"meta":{
"title":"articleCreate",
"icon":"article-create"
}
},
{
"path":"/article/editor/:id",
"meta":{
"title":"articleEditor"
}
}
]
}
]
要渲染出菜单,我们需要菜单标签和菜单图片,它对应的值保存在meta中,在我们想要的路由表中,没有meta的数据不应该存在,而且不应有重复的路由数据
我们理想中的路由表如下
[
{ title: '个人中心', path: '' },
{
title: '用户',
children: [
{ title: '员工管理', path: '' },
{ title: '角色列表', path: '' },
{ title: '权限列表', path: '' }
]
},
{
title: '文章',
children: [
{
title: '文章排名',
path: ''
},
{
title: '创建文章',
path: ''
}
]
}
]
首先我们先去除重复的路由数据,可以看到重复的都是子路由数据,子路由数据在他们的父路由数据的那一层也存在,我们要把他们去除掉。
对此可以先获取所有的子路由的数组,再遍历整个路由表,如果数组中含有该路由,就把这个路由去除
在根目录下创建utils/route.js,定义去除重复路由的方法filterRouters
// 去除重复路由
export const filterRouters = routes => {
const childrenRoutes = getChildrenRoutes(routes)
return routes.filter(route => {
// 如果子路由数组中含有route,就不返回
return !childrenRoutes.find(childrenRoute => {
return childrenRoute.path === route.path
})
})
}
// 获取所有子路由
const getChildrenRoutes = routes => {
const result = []
routes.forEach(route => {
if (route.children && route.children.length > 0) {
result.push(...route.children)
}
})
return result
}
去除后的数据如图
其次应该把不含有meta的数据也去除掉,有以下三种情况
- 不存在 children 并且 不存在 meta
直接return
- 存在 children 不存在 meta
迭代children
- 存在meta存在children 存在meta无children
需要加到一级路由,还要迭代children
export function generateMenus(routes, basePath = '') {
const result = []
// 遍历路由表
routes.forEach((item) => {
// 不存在 children && 不存在 meta 直接 return meta存在说明是菜单项 children说明有子项
if (isNull(item.meta) && isNull(item.children)) return
// 存在 children 不存在 meta,进入迭代
if (isNull(item.meta) && !isNull(item.children)) {
result.push(...generateMenus(item.children))
console.log(generateMenus(item.children))
return
}
// 有meta有children 有meta无children
// 合并 path 作为跳转路径
// console.log('我的: ' + item.path)
const routePath = path.resolve(basePath, item.path)
// console.log('合并: ' + routePath)
// 路由分离之后,存在同名父路由的情况,需要单独处理
let route = result.find((item) => item.path === routePath) // /user
if (!route) {
route = {
...item,
path: routePath,
children: []
}
// icon 与 title 必须全部存在
if (route.meta.icon && route.meta.title) {
// meta 存在生成 route 对象,放入 arr
result.push(route)
}
}
// 存在 children 进入迭代到children
if (item.children) {
route.children.push(...generateMenus(item.children, route.path))
}
})
return result
}
最后得到我们理想中的路由表
接下来渲染即可