动态加载路由遇到的问题及解决办法

588 阅读2分钟

1.问题代码

在使用ts封装动态添加路由对象时,遇到的因为异步导致数据为空的问题。

先看代码:

    // userMenus => routes
      const routes = mapMenusToRoutes(userMenus)
​
      // 将routes => router.main.children
      routes.forEach((route) => {
        router.addRoute("main", route)
      })
import { Datum } from "@/service/loginApi/types"
import { RouteRecordRaw } from "vue-router"export function mapMenusToRoutes(userMenus: Datum[]): RouteRecordRaw[] {
  const routes: RouteRecordRaw[] = []
​
  // 1. 先去加载默认所有的routes
  const allRoutes: RouteRecordRaw[] = []
  const routeFiles = require.context("../router/main", true, /.ts/)
  routeFiles.keys().forEach(async (key) => {
    const { default: route } = await require("../router/main" + key.split(".")[1])
    allRoutes.push(route)
  })
​
  // 2. 根据菜单获取需要添加的routes
  const _recurseGetRoute = (menus: Datum[] | any[]) => {
    for (const menu of menus) {
      if (menu.type === 2) {
        const route = allRoutes.find((route) => route.path === menu.url)
        if (route) routes.push(route)
      } else {
        _recurseGetRoute(menu.children)
      }
    }
  }
  _recurseGetRoute(userMenus)
  console.log(routes)
​
  return routes
}

乍一看,思路可能还比较清晰

  1. 传入菜单对象到目的函数
  2. 扫描在router/main路径下所有的声明组件的ts文件,并将路径添加到全部的路由对象数组中
  3. 使用递归函数,将type等于2的添加到待使用的路由对象中
  4. 最后返回routes对象

问题在于: 引入组件模块对象时,需要使用异步的方式

导致:

  1. allRoutes对象在递归函数的同步代码中是一个空的数组
  2. allRoutes无法和menus里面对象进行匹配,导致返回的routes数组也是个空的

image-20220215145831815.png

2.解决办法

  • 让所有的组件模块加载完毕后,再去调用同步的递归函数
  • return 语句也是个同步的代码,在处理异步的时候已经将routes返回出去了;因此需要将routes异步输出

解决代码:

  1. 判断出所有路由对象和所有组件模块相同的长度时,再去调用回调的代码,取到allRoutes数组中的数据是所有的
  2. 将返回值设置成promise,通过resolve方法将数据异步输出,确保routes对象是根据回调函数执行之后的对象
export function mapMenusToRoutes(
  userMenus: Datum[]
): Promise<RouteRecordRaw[]> {
  const routes: RouteRecordRaw[] = []
​
  // 1. 先去加载默认所有的routes
  const allRoutes: RouteRecordRaw[] = []
  const routeFiles = require.context("../router/main", true, /.ts/)
  routeFiles.keys().forEach(async (key) => {
    const { default: route } = await require("../router/main" +
      key.split(".")[1])
    allRoutes.push(route)
    // 加载完所有的文件后,再去执行菜单筛选
    if (allRoutes.length === routeFiles.keys().length) {
      _recurseGetRoute(userMenus)
    }
  })
​
  // 2. 根据菜单获取需要添加的routes
  const _recurseGetRoute = (menus: Datum[] | any[]) => {
    for (const menu of menus) {
      if (menu.type === 2) {
        const route = allRoutes.find((route) => route.path === menu.url)
        if (route) routes.push(route)
      } else {
        _recurseGetRoute(menu.children)
      }
    }
  }
  // 同步代码会被文件扫描的异步事件影响,导致数据量return时数据量为空
  // 改成promise,异步任务,任务队列在加载事件之后
  return Promise.resolve(routes)
}
  1. 返回的是promise对象,则需要通过.then函数或者await来获取到resolve中输出的数据
      // userMenus => routes
      mapMenusToRoutes(userMenus).then((routes) => {
          console.log(routes)
        // 将routes => router.main.children
        routes.forEach((route) => {
          router.addRoute("main", route)
        })

这样就获取到了想要的路由对象数组

image-20220215145533872.png

3.总结

在面对异步代码的时候,尤其需要小心涉及到同步代码中使用异步中处理到的变量数据量为空的问题!