vue2 动态路由的实现
概述
一般情况下,路由都是前端约定好的,但是每当项目发布上线,或者客户需求新的页面的时候,都需要做出路由改变。这样运维就可以现场支持,方便做出可操作的中户中台,来管理我们的中心项目登录及权限,路由等方面;
这样就必须使用动态生成路由,由后台返回具体的路由结构体;我这边是这样模拟的;
[
{
"id": 612,
"menuName": "测试数据1",
"pid": 0,
"url": "HOME_REMOTE_INTERROGATION",
"menuType": "1",
"visible": 1,
"isRefresh": 1,
"perms": null,
"projectSign": "pt-osc",
"bind": null,
"sort": 0,
"children": []
},
{
"id": 605,
"menuName": "测试数据2",
"pid": 0,
"url": "HOME_WARNING_MANAGEMENT",
"menuType": "1",
"visible": 1,
"isRefresh": 1,
"perms": null,
"projectSign": "pt-osc",
"bind": null,
"sort": 0,
"children": [
{
"id": 653,
"menuName": "测试数据1子集",
"pid": 605,
"url": "/region",
"menuType": "0",
"visible": 1,
"isRefresh": 1,
"perms": null,
"projectSign": "pt-osc",
"bind": null,
"sort": null,
"children": []
},
{
"id": 652,
"menuName": "测试数据1子集",
"pid": 605,
"url": "/roomList",
"menuType": "0",
"visible": 1,
"isRefresh": 1,
"perms": null,
"projectSign": "pt-osc",
"bind": null,
"sort": null,
"children": []
}
]
},
]
这个值是跟后台约定好的,通过这个值来寻找总路由的父路由路由匹配;(也许现在很懵,继续往下看!)
第一步首先需要将项目需要的路由在此文件处理一边,创建 router.js 文件,内容如下(下方文件代码可复制使用,针对不同数据结果做出调整即可):
引入路由 vue 组件(望理解打码!)
// 首先需要将项目中 所有路由准备好!(包括所有父路由,子路由) 这里只是测试数据,真实场景需要定义所有的路由!
const routerArr = [
{
path: '/home',
name: 'OneStop',
component: OneStop,
meta: {
urlType: 'BCDEFG',
},
children: [
{
path: '/one',
name: 'One',
component: Special,
},
{
path: '/test1',
name: 'test1',
component: () =>
import('@/views/oneStop/components/Test1.vue'),
meta: {
title: '测试页面1',
},
},
{
path: '/test2',
name: 'test2',
component: () =>
import('@/views/oneStop/components/Test2.vue'),
meta: {
title: '随笔写2',
},
},
],
},
{
path: '/monitor',
name: 'monitorIndex',
redirect: '/monitor/region',
component: () => import('@/views/monitor/index.vue'),
meta: {
title: '的撒发达',
urlType: 'CDFGHFADF',
},
children: [
{
path: '/region',
name: 'region',
component: () => import('@/views/monitor/components/testTwo.vue'),
meta: {
title: '第二个路由1',
},
},
{
path: '/roomList',
name: 'roomList',
component: () =>
import('@/views/monitor/components/testTwo.vue'),
meta: {
title: '第二个路由2',
},
},
],
},
]
// 这个方法是使用递归的形式 作用:将所有嵌套的子路由进行打平, 且把 path路径当作key值进行约定!
function flatRouters(data) {
const config = {}
data.forEach((rItem) => {
let childrenConfig = {}
// 先判断是否有 子集
if (rItem.children?.length) {
childrenConfig = flatRouters(rItem.children)
}
if (!rItem.meta || !rItem.meta.urlType)
config[rItem.path] = {
...rItem,
children: [],
}
Object.assign(config, childrenConfig)
})
return config
}
// 声明变量接受 打平 后的子路由对象数据
const confing = flatRouters([...routerArr])
const _routerPack = {}
// 将所有父路由的 urlType 作为key值, value等于本身
routerArr.forEach((item) => {
_routerPack[item.meta.urlType] = {
...item,
children: [],
}
})
// 在处理过后的 config 对象中 匹配数据 拿传入的路径,去匹配config的数据,取出config的数据。
function setRouter(menu) {
const keys = Object.keys(confing)
const menuKey = keys.find((key) => key.includes(menu.url))
if (menuKey) {
const dataR = confing[menu.url]
if (dataR) return dataR
}
return false
}
// 递归的去匹配路由数据 (也就是去匹配 config 中的数据取出来生成新数组)
function getRouter(menuData) {
const rArr = []
const typeKeys = Object.keys(_routerPack)
menuData.forEach((menuItem) => {
// 判断是否为大类
if (menuItem.menuType === '1') {
const key = typeKeys.find((k) => k.includes(menuItem.url))
if (key) {
const children = menuItem.children ? getRouter(menuItem.children) : []
const rCun = rArr.find((item) => item.meta.urlType === key)
if (rCun) {
const chil = children.filter((item) => {
return rCun.children.find((i) => i.name !== item.name)
})
rCun.children.push(...chil)
} else {
const menu = _routerPack[key]
menu.children = children
rArr.push(menu)
}
}
} else {
// 此时为 路由数据
// 先判断 是否存在子集
const childrenArr = []
if (menuItem.children.length) {
menuItem.children.forEach((childrenItem) => {
const cItem = setRouter(childrenItem)
if (cItem) childrenArr.push(cItem)
})
}
const rItem = setRouter(menuItem)
if (rItem) {
rItem.children = []
rItem.children.push(...childrenArr)
rArr.push(rItem)
}
}
})
return rArr
}
// 调用方法
function addRouter() {
const projectData = JSON.parse(sessionStorage.getItem('projectData'))
if (projectData) {
const permList = projectData.permList ?? []
return getRouter(permList)
}
return []
}
export { routerArr, confing, addRouter }
第二步路由总文件中,也就是 目录 router 下的 index.js文件(内容约定如下):
import Vue from 'vue'
import VueRouter from 'vue-router'
import Login from '@/views/login'
// 引入刚才声明的方法
import { addRouter, routerArr } from './router'
Vue.use(VueRouter)
let addRoute = []
// 因为做了配置 根据isDynamicRouting值,是否动态路由,是的话调用addRouter方法, 不是直接赋值原本路由即可!
if (window.ENV.isDynamicRouting) {
addRoute = addRouter()
} else {
addRoute = routerArr
}
// 这些是固定路由,登陆页面,404页面
const routes = [
{
path: '/login',
name: 'Login',
components: {
default: Login,
child: null,
},
},
{
path: '/',
redirect: '/home',
},
{
path: '/mock/videoPlayer',
name: 'videoPlayer',
component: () => import('@/views/mock/videoPlayer'),
},
]
// 导出方法 注册路由
export function createRouter(r = []) {
return new VueRouter({
mode: 'history',
base: import.meta.env.VITE_PUBLIC_PATH || '/',
scrollBehavior: () => ({ y: 0 }),
routes: [...r, ...routes],
})
}
const router = createRouter(addRoute)
// 全局导航守卫
router.beforeEach(async (to, form, next) => {
const { token } = sessionStorage
const { sfzh } = to.query
if (!token && to.name !== 'Login') {
if (window.ENV.isDynamicRouting) {
// 这里是为避免重复路由的问题
const newRouter = createRouter()
router.matcher = newRouter.matcher
}
if (sfzh) next({ name: 'Login', query: { ...to.query, toPath: to.path } })
next('/login')
}
next()
})
export default router
然后再总入口文件main.js中 注册即可(这里只粘贴相关代码):
import router from './router'
new Vue({
router,
store,
render: (h) => h(App),
}).$mount('#app')
最后一步:
登录成功后记得要将 最上方等你们约定数据结构放到 缓存当中哦!因为这里:
// 动态路由
if (window.ENV.isDynamicRouting) {
const router = addRouter()
router.forEach((item) => {
this.$router.addRoute(item)
})
}
注意:isDynamicRouting 这个值决定了是否使用动态路由,声明在全局里面!
退出时需要置空路由,重新指定:
到此就结束啦!对你有帮助的话留下你的收藏点赞哦!谢谢!