自学内容网 自学内容网

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 这个值决定了是否使用动态路由,声明在全局里面!

退出时需要置空路由,重新指定:
在这里插入图片描述

到此就结束啦!对你有帮助的话留下你的收藏点赞哦!谢谢!


原文地址:https://blog.csdn.net/weixin_50559423/article/details/143770126

免责声明:本站文章内容转载自网络资源,如本站内容侵犯了原著者的合法权益,可联系本站删除。更多内容请关注自学内容网(zxcms.com)!