当前位置 博文首页 > wusq的博客:vue根据后端菜单数据生成动态路由
动态路由初体验,存在不足,欢迎点评指正~
前言:在之前的项目中,菜单是动态获取的,而路由是写死的,配置路由的时候只要保证路由的path与菜单的index(elementUI的el-menu组件)相同就可以实现路由跳转,只是菜单改了的话,路由也得相应修改,否则就会找不到页面。当时之所以没有研究动态路由是觉得跳转的页面是路由指定的,如果路由变成动态获取的话,前端的页面文件命名和目录结构都得根据后端的数据调整,也不是很灵活,但是…最近后端调整了数据,发现好多路由没有匹配到,都跳转到了一个重定向/redirect页面,所以最终还是决定动手研究动态路由!
name -> componentName
path -> url
component -> 由上级菜单的componentName加上本级菜单的componentName组成文件路径
meta的title -> menuName
具体代码:
/*
* @Author: Wushiqi
* @Descripttion: 获取路由:遍历传入的菜单列表,拿取拼接路由所需的数据(path、name、title、component)
* @Date: 2020-08-19 10:44:15
* @LastEditor: Wushiqi
* @LastEditTime: 2020-08-25 16:07:33
*/
// Layout组件是项目中的主页面,切换路由时,仅切换Layout中的组件
import Layout from '@/layout'
export function getAsyncRoutes(routes) {
const res = []
routes.forEach(route => { // 所有菜单都是二级结构,一级没有页面功能,所以只要添加二级菜单的路由
if (route.childMenuInfoTreeSet.length !== 0) {
const children = []
route.childMenuInfoTreeSet.forEach(menu => { // 二级菜单需匹配页面
children.push({
path: menu.url,
name: menu.componentName.split('/')[1],
// 此处使用require,由于import会有奇怪的错误
component: (resolve) => require([`@/views${route.componentName + menu.componentName}`], resolve),
meta: { title: menu.menuName }
})
})
res.push({
path: route.url,
component: Layout,
children: children
})
}
})
return res
}
逻辑:首次登录时获取菜单,同时执行getAsyncRoutes方法获取路由表,将得到的路由数据使用addRoutes添加到路由表,并且将每一项路由push到路由的options。注意一个坑:f5刷新页面时,会执行路由的跳转的操作,而再次获取菜单和路由这个操作是在路由跳转之后的,所以在刷新的时候就找不到起始的路由(除了写死的路由之外),解决办法是让再次获取菜单和路由的操作在路由跳转之前,我这边的思路是在第一次获取菜单时,将菜单存到sessionStorage,之后刷新的时候不再访问接口重新获取。
这是写死的路由(未添加动态数据):
这是添加了动态数据之后的路由:?
获取菜单部分代码:
async created() {
if (!sessionStorage.menu) { // 首次登陆获取菜单列表
const response = await publicAPI.userMenuAPI() // 从后台获取菜单列表
const data = response.data.data.menuList
if (data.length > 0) {
this.menuList = data
const router = getAsyncRoutes(data) // 根据菜单提取路由
this.$router.addRoutes(router)
router.forEach(val => { // 将菜单提取出来的路由加到路由表
this.$router.options.routes.push(val)
})
console.log(this.$router);
sessionStorage.setItem('menu', JSON.stringify(data)) // 将菜单列表存到sessionStorage
}
} else { // 已有菜单不再重新获取
this.menuList = JSON.parse(sessionStorage.menu)
// 要重新将路由挂上去,否则点击菜单不会跳转页面
const router = getAsyncRoutes(this.menuList)
this.$router.addRoutes(router)
router.forEach(val => { // 将菜单提取出来的路由加到路由表
this.$router.options.routes.push(val)
})
}
}
根据下面拼接的结果调整目录
比如用户管理拼接完成是@/views/baseData/user,则目录是这个样子的?
我的默认路由有一项是重定向,所有匹配不到的路由都会跳转到重定向页面。
改为动态路由之后发现,刷新的时候路由会跳转到重定向页面,所以要在重定向页面让路由跳转回原来的页面。
{
path: '*',
redirect: '/redirect/:path*'
},
{
path: '/redirect',
name: 'redirect',
component: Layout,
hidden: true,
children: [
{
path: '/redirect/:path*',
component: () => import('@/views/redirect/redirect')
}
]
},
??重定向页面代码:
<script>
export default {
created() { // 重定向页面
const { params } = this.$route
const { path } = params
if (path) {
this.$router.push({ path: `/${path}` })
} else {
this.$router.push({ path: `${sessionStorage.toPath}` })
}
},
render: function(h) {
return h() // avoid warning message
}
}
</script>
重定向页面的sessionStorage.toPage存的是刷新前访问的页面,相关代码添加在router.beforeEach里面
if (!to.redirectedFrom && to.path !== '/redirect') { // 如果不是重定向操作,就存下当前访问的路由地址
sessionStorage.setItem('toPath', to.path)
}
这样就完成动态路由啦~ 只是这样以后,刷新页面会先跳转至redirect页面,然后再跳转回原先的页面,刷新的速度变慢了,视觉上也会看见一闪的白屏,寻求更好的方法~
cs