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
中判断,如果routeReady
为false
则处理路由,处理完成后跳转。创建一个Loading页面,如果路由没有匹配的地址则跳转至Loading页面,并在该页面进行判断:
如果
routeReady
为true
,说明去往的地址并不在该用户的权限菜单中,转向404页面如果
routeReady
为false
,则说明路由未加载完成,那么就在当前页面等待,等routeReady
为true
时,再执行上面的判断
笔者这里用了方法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")
}
}
})
通过这种方式,可以在用户刷新页面后有一个顺滑的体验。
进入第一个路由
目前还存在一个问题,用户在登录跳转后,会进入后台页面,但是此时不会进入到任一菜单中:
而我们希望登录跳转后能自动进入到第一个菜单,即:
因此我们需要一个方法来找到第一个可用的路由:
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


猜你喜欢
- MSXML是微软非托管代码栈中最为核心的XML服务集合,不但适合基于COM的开发应用,更是微软AJAX解决方案和客户端XSLT解决方案的核心
- 阅读上一篇教程:WEB2.0网页制作标准教程(9)第一个CSS布局实例如果我们想在3列布局的最后加一行页脚,放版权之类的信息。就遇到必须对齐
- 基础知识# 在Linux操作系统下,Python3的默认环境编码变为了utf-8编码,所以在编写代码的时候,字符串大部分都是以utf-8处理
- 这篇文章主要介绍了如何获取Python简单for循环索引,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的
- 有时表或结果集包含重复的记录。有时它是允许的,但有时它需要停止重复的记录。有时它需要识别重复的记录从表中删除。本章将介绍如何防止发生在一个表
- 一、原理说明1,authentication_string这是Mysql8.0新做出的修改,在旧版本中使用的是password()函数。2,
- 因为工作(懒惰),几年了,断断续续学习又半途而废了一个又一个技能。试着开始用博客记录学习过程中的问题和解决方式,以便激励自己和顺便万一帮助了
- 介绍观察者模式:是一种行为型设计模式。主要关注的是对象的责任,允许你定义一种订阅机制,可在对象事件发生时通知多个"观察"
- 环境搭建1、下载所需的软件包:(1)python安装包(2)django安装包以下2个包其实是安装python包管理工具,在后面安装djan
- 我最近花了一些时间在探索CPython,并且我想要在这里分享我的一些冒险经历。Allison Kaptur的excellent guide
- K线图概念股市及期货市bai场中的K线图的du画法包含四个zhi数据,即开盘dao价、最高价、最低价zhuan、收盘价,所有的shuk线都是
- 上次亚马逊的商品信息都获取到了,自然要看一下评论的部分。用户的评论能直观的反映当前商品值不值得购买,亚马逊的评分信息也能获取到做一个评分的权
- 如何用Response.Write调用代替内嵌表达式?我们可以利用下面的代码,注意:代码的每一行对响应流有一次写操作,所有的代码都包含在一个
- MooTools 1.2的整理排序类Sortables原文地址:30 Days of Mootools 1.2 Tutorials - Da
- 安装Python2.7后,它自带一个编辑器IDLE,但是使用几次之后出现启动不了的情况,可做如下操作。Windows操作系统下,使用快捷键
- 1.网络获取Google图像1.1 google_images_downloadPython 是一种多用途语言,广泛用于脚本编写。我们可以编
- access中可以将文本中的数据轻松导入表中,mysql中用起来没那么方便,其实起来也很简单。首先将数据记录按行处理好用特定的字符分开如:“
- 发帖或者回帖的时候,系统会提示银两或经验增加的效果,慢慢出现又慢慢消失,用于取代对话框的那种是如何实现的?用google的jquery ap
- asp.net的dropdownlist控件为我们的web应用提供了许多用处,但有一点我总感觉不爽的就是在使用dropdownlist的事件
- 本文实例讲述了Python实现螺旋矩阵的填充算法。分享给大家供大家参考,具体如下:afanty的分析:关于矩阵(二维数组)填充问题自己动手推