Vue3中简单实现动态添加路由

作者:bald3r 时间:2023-07-02 16:58:45 

前言

通过后端接口的返回值,动态添加路由,是作为权限控制的一种常见方式,本文将简单讲解如何在Vue3中动态添加路由。

示例数据

[
 {
   "id": 1,
   "pid": 0,
   "url": "/dashboard",
   "title": "控制面板板",
   "component": "/src/views/dashboard/DashboardView.vue",
   "icon": "SlidersOutlined",
   "is_show": 0,
   "level": 1,
   "sort": 1,
   "order": "1",
   "type": "menu",
   "status": 0,
   "children": null
 },
 {
   "id": 2,
   "pid": 0,
   "url": "/system",
   "title": "系统设置",
   "component": null,
   "icon": "ToolOutlined",
   "is_show": 0,
   "level": 1,
   "sort": 7,
   "order": "2",
   "type": "menu_dir",
   "tips": null,
   "status": 0,
   "children": [
     {
       "id": 7,
       "pid": 2,
       "url": "/system/menu",
       "title": "菜单管理",
       "component": "/src/views/system/MenuView.vue",
       "icon": "BarsOutlined",
       "is_show": 0,
       "level": 2,
       "sort": 3,
       "order": "2,7",
       "type": "menu",
       "tips": null,
       "status": 0,
       "children": [
         {
           "id": 8,
           "pid": 7,
           "url": "/system/menu/add",
           "title": "新增菜单",
           "component": null,
           "icon": null,
           "is_show": 1,
           "level": 3,
           "sort": 1,
           "order": "2,7,8",
           "type": "button",
           "tips": null,
           "status": 0,
           "children": null
         }
       ]
     }
   ]
 },
]

思路分析

动态添加路由的实质,就是先将后端返回的json数据转化成一个个RouteRecordRaw形式的对象,然后调用Vue Router的addRoute方法,添加进路由列表中。由于每个路由地址都会对应一个Vue组件,因此还需要将Vue组件都通过import.meta.glob读取到内存中。

具体实现函数

const viewsComponent: Record<string, any> = import.meta.glob("/src/views/**/*.vue", { eager: true })
const addRouteAll = (menu: RoleMenu[]) => {  //RoleMenu就是接口返回的数据的类型
   menu.forEach(item => {
       if (item.type === "menu" && viewsComponent[item.component]) {
           addRouteItem(item)
       }
       if (item.children && item.children.length > 0) {
           addRouteAll(item.children)
       }
   })
}
const addRouteItem = (route: RoleMenu) => {
   const path = route.url
   const component = viewsComponent[route.component]
   const routeBaseInfo: RouteRecordRaw = {
       path,
       name: path.substring(1),
       component: component.default,
       meta: {
           title: route.title,
           icon: route.icon,
           keepalive: route.children && route.children.length > 0 ? 0 : path.substring(1),
           menu_type: "tab",
           type: route.type,
           url: route.url,
           addTab: true
       }
   }
   router.addRoute(routeBaseInfo)
}

存在问题

路由何时处理?

笔者一开始认为,登录成功后立刻调用获取菜单的接口,然后处理路由,因此路由的处理应该在登录页面中的登录请求成功后进行处理,但是此时存在一个问题,用户登录成功进入后台页面,然后用户刷新页面,就会提示导航失败,控制台也会报错,因此笔者认为应该在登录成功进入后台页面之后开始处理。

笔者后台的主体页面框架为MainLayout,因此笔者在此进行路由处理。

const getMenu = () => {
   apiAuthMenuList().then(res => {
       menuList.value = handleMenu(res.content)  //菜单处理
       addRouteAll(res.content)
   })
}
onMounted(() => {
   getMenu()
})

导航失败

⚠️ [Vue Router warn] : No match found for location with path "/dashboard"

这是因为路由跳转的时机要早于组件挂载,因此在组件挂载并处理路由前,路由就已经跳转并报错了。

笔者解决这个问题的思路有两个:

  • 首先定义全局变量routeReady,初始值为false,当路由处理完成后变为true

  • 在路由守卫beforeEach中判断,如果routeReadyfalse则处理路由,处理完成后跳转。

  • 创建一个Loading页面,如果路由没有匹配的地址则跳转至Loading页面,并在该页面进行判断:

    • 如果routeReadytrue,说明去往的地址并不在该用户的权限菜单中,转向404页面

    • 如果routeReadyfalse,则说明路由未加载完成,那么就在当前页面等待,等routeReadytrue时,再执行上面的判断

笔者这里用了方法2。

//截获所有未匹配的路由,进入Loading页面
{
   path: "/:pathMatch(.*)*",
   component: () => import("../views/LoadingView.vue")
}
//LoadingView.vue
watchEffect(() => {
   if (globalStore.routeReady) {
       const routeList = router.getRoutes()
       if (routeList.find(i => i.path === router.currentRoute.value.fullPath)) {
           router.push(router.currentRoute.value.fullPath)
       } else {
           router.push("/notFound")
       }
   }
})

通过这种方式,可以在用户刷新页面后有一个顺滑的体验。

进入第一个路由

目前还存在一个问题,用户在登录跳转后,会进入后台页面,但是此时不会进入到任一菜单中:

Vue3中简单实现动态添加路由

而我们希望登录跳转后能自动进入到第一个菜单,即:

Vue3中简单实现动态添加路由

因此我们需要一个方法来找到第一个可用的路由:

const getFirstRoute = (routes: RouteRecordRaw[]): false | RouteRecordRaw => {
   const routerPaths: string[] = []
   const routers = router.getRoutes()
   routers.forEach(item => {
       if (item.path) routerPaths.push(item.path)
   })
   let find: boolean | RouteRecordRaw = false
   for (const key in routes) {
       if (routes[key].meta?.type != "menu_dir" && routerPaths.indexOf(routes[key].path) !== -1) {
           return routes[key]
       } else if (routes[key].children && routes[key].children?.length) {
           find = getFirstRoute(routes[key].children!)
           if (find) return find
       }
   }
   return find
}

然后调用这个方法即可:

const init = () => {
   const firstRoute = getFirstRoute(menuList.value!)
   if (firstRoute) {
       router.push(firstRoute.path)
   }
}
onMounted(() => {
   init()
})

后记

来源:https://juejin.cn/post/7238795712569245753

标签:Vue3,动态,路由
0
投稿

猜你喜欢

  • javascript显示动态时间的方法汇总

    2024-05-02 17:31:27
  • 深入浅析Python数据分析的过程记录

    2022-05-04 02:56:27
  • Python 正则 re.compile 真的必需吗

    2021-12-27 17:12:24
  • Web页脚设计的版权格式规范

    2009-07-22 20:52:00
  • ASP网站数据采集经验谈

    2008-03-09 15:30:00
  • el-input无法输入的问题和表单验证失败问题解决

    2024-04-09 10:48:37
  • FCKeditor ASP.NET 上传附件研究

    2023-12-30 05:27:22
  • python+opencv实现堆叠图片

    2023-07-19 21:22:45
  • Python实现屏幕截图的代码及函数详解

    2023-06-19 11:30:29
  • ASP实现上传图片到数据库

    2007-09-21 12:59:00
  • Python reflect单例模式反射各个函数

    2022-11-11 08:52:51
  • python基础教程之csv文件的写入与读取

    2021-06-18 21:21:00
  • Python单链表的简单实现方法

    2021-08-14 01:58:33
  • ASP获取网页内容(解决乱码问题)

    2009-07-26 10:44:00
  • Python socket实现的文件下载器功能示例

    2021-03-12 22:43:19
  • python3模拟实现xshell远程执行linux命令的方法

    2022-05-26 11:45:14
  • 关于淘宝网导航几个让人不解的问题

    2009-03-24 21:08:00
  • Python的多态性实例分析

    2023-04-15 04:37:18
  • python连接mongodb操作数据示例(mongodb数据库配置类)

    2023-05-01 17:21:17
  • python 判断网络连通的实现方法

    2021-07-28 06:47:29
  • asp之家 网络编程 m.aspxhome.com