Vue3中簡(jiǎn)單實(shí)現(xiàn)動(dòng)態(tài)添加路由
前言
通過(guò)后端接口的返回值,動(dòng)態(tài)添加路由,是作為權(quán)限控制的一種常見(jiàn)方式,本文將簡(jiǎn)單講解如何在Vue3中動(dòng)態(tài)添加路由。
示例數(shù)據(jù)
[
{
"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": "系統(tǒng)設(shè)置",
"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
}
]
}
]
},
]思路分析
動(dòng)態(tài)添加路由的實(shí)質(zhì),就是先將后端返回的json數(shù)據(jù)轉(zhuǎn)化成一個(gè)個(gè)RouteRecordRaw形式的對(duì)象,然后調(diào)用Vue Router的addRoute方法,添加進(jìn)路由列表中。由于每個(gè)路由地址都會(huì)對(duì)應(yīng)一個(gè)Vue組件,因此還需要將Vue組件都通過(guò)import.meta.glob讀取到內(nèi)存中。
具體實(shí)現(xiàn)函數(shù)
const viewsComponent: Record<string, any> = import.meta.glob("/src/views/**/*.vue", { eager: true })
const addRouteAll = (menu: RoleMenu[]) => { //RoleMenu就是接口返回的數(shù)據(jù)的類型
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)
}存在問(wèn)題
路由何時(shí)處理?
筆者一開(kāi)始認(rèn)為,登錄成功后立刻調(diào)用獲取菜單的接口,然后處理路由,因此路由的處理應(yīng)該在登錄頁(yè)面中的登錄請(qǐng)求成功后進(jìn)行處理,但是此時(shí)存在一個(gè)問(wèn)題,用戶登錄成功進(jìn)入后臺(tái)頁(yè)面,然后用戶刷新頁(yè)面,就會(huì)提示導(dǎo)航失敗,控制臺(tái)也會(huì)報(bào)錯(cuò),因此筆者認(rèn)為應(yīng)該在登錄成功進(jìn)入后臺(tái)頁(yè)面之后開(kāi)始處理。
筆者后臺(tái)的主體頁(yè)面框架為MainLayout,因此筆者在此進(jìn)行路由處理。
const getMenu = () => {
apiAuthMenuList().then(res => {
menuList.value = handleMenu(res.content) //菜單處理
addRouteAll(res.content)
})
}
onMounted(() => {
getMenu()
})導(dǎo)航失敗
?? [Vue Router warn] : No match found for location with path "/dashboard"
這是因?yàn)槁酚商D(zhuǎn)的時(shí)機(jī)要早于組件掛載,因此在組件掛載并處理路由前,路由就已經(jīng)跳轉(zhuǎn)并報(bào)錯(cuò)了。
筆者解決這個(gè)問(wèn)題的思路有兩個(gè):
- 首先定義全局變量
routeReady,初始值為false,當(dāng)路由處理完成后變?yōu)?code>true - 在路由守衛(wèi)
beforeEach中判斷,如果routeReady為false則處理路由,處理完成后跳轉(zhuǎn)。 - 創(chuàng)建一個(gè)Loading頁(yè)面,如果路由沒(méi)有匹配的地址則跳轉(zhuǎn)至Loading頁(yè)面,并在該頁(yè)面進(jìn)行判斷:
- 如果
routeReady為true,說(shuō)明去往的地址并不在該用戶的權(quán)限菜單中,轉(zhuǎn)向404頁(yè)面 - 如果
routeReady為false,則說(shuō)明路由未加載完成,那么就在當(dāng)前頁(yè)面等待,等routeReady為true時(shí),再執(zhí)行上面的判斷
- 如果
筆者這里用了方法2。
//截獲所有未匹配的路由,進(jìn)入Loading頁(yè)面
{
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")
}
}
})通過(guò)這種方式,可以在用戶刷新頁(yè)面后有一個(gè)順滑的體驗(yàn)。
進(jìn)入第一個(gè)路由
目前還存在一個(gè)問(wèn)題,用戶在登錄跳轉(zhuǎn)后,會(huì)進(jìn)入后臺(tái)頁(yè)面,但是此時(shí)不會(huì)進(jìn)入到任一菜單中:

而我們希望登錄跳轉(zhuǎn)后能自動(dòng)進(jìn)入到第一個(gè)菜單,即:

因此我們需要一個(gè)方法來(lái)找到第一個(gè)可用的路由:
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
}然后調(diào)用這個(gè)方法即可:
const init = () => {
const firstRoute = getFirstRoute(menuList.value!)
if (firstRoute) {
router.push(firstRoute.path)
}
}
onMounted(() => {
init()
})后記
到此這篇關(guān)于Vue3中簡(jiǎn)單實(shí)現(xiàn)動(dòng)態(tài)添加路由的文章就介紹到這了,更多相關(guān)Vue3動(dòng)態(tài)添加路由內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
在vue中給后臺(tái)接口傳的值為數(shù)組的格式代碼
這篇文章主要介紹了在vue中給后臺(tái)接口傳的值為數(shù)組的格式代碼,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2020-11-11
Vue3全局組件注冊(cè)的實(shí)現(xiàn)代碼
在這篇文章中,我們將學(xué)習(xí)一下 Vue3 的全局組件注冊(cè)是如何實(shí)現(xiàn)的,文中通過(guò)代碼示例給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作有一定的幫助,需要的朋友可以參考下2023-12-12
Vue實(shí)現(xiàn)手機(jī)號(hào)、驗(yàn)證碼登錄(60s禁用倒計(jì)時(shí))
這篇文章主要介紹了Vue實(shí)現(xiàn)手機(jī)號(hào)、驗(yàn)證碼登錄(60s禁用倒計(jì)時(shí)),幫助大家更好的理解和使用vue,感興趣的朋友可以了解下2020-12-12
vue/cli3.0腳手架部署到nginx時(shí)頁(yè)面空白的問(wèn)題及解決
這篇文章主要介紹了vue/cli3.0腳手架部署到nginx時(shí)頁(yè)面空白的問(wèn)題及解決方案,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-10-10
在vue中獲取微信支付code及code被占用問(wèn)題的解決方法
這篇文章主要介紹了在vue中獲取微信支付code及code被占用問(wèn)題的解決方法。小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2019-04-04
vee-validate vue 2.0自定義表單驗(yàn)證的實(shí)例
今天小編就為大家分享一篇vee-validate vue 2.0自定義表單驗(yàn)證的實(shí)例,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2018-08-08
vue項(xiàng)目中極驗(yàn)驗(yàn)證的使用代碼示例
這篇文章主要介紹了vue項(xiàng)目中極驗(yàn)驗(yàn)證的使用代碼示例,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2019-12-12

