vue3+vue-router+vite實(shí)現(xiàn)動(dòng)態(tài)路由的全過(guò)程
什么是動(dòng)態(tài)路由

什么場(chǎng)景會(huì)用到動(dòng)態(tài)路由
舉一個(gè)最常見(jiàn)的例子,比如說(shuō)我們要開(kāi)發(fā)一個(gè)后臺(tái)管理系統(tǒng),一般來(lái)說(shuō)后臺(tái)管理系統(tǒng)都會(huì)分角色登錄,這個(gè)時(shí)候也就涉及到了權(quán)限,比如說(shuō)這個(gè)后臺(tái)管理系統(tǒng)現(xiàn)在有超級(jí)管理員,管理員,運(yùn)維,財(cái)務(wù)等這幾個(gè)角色,每個(gè)角色登錄系統(tǒng)之后都會(huì)有不同的權(quán)限,超級(jí)管理員需要所有的權(quán)限,財(cái)務(wù)可能只需要財(cái)務(wù)相關(guān)的模塊(菜單)以及按鈕等,通常實(shí)現(xiàn)這種需求會(huì)有以下常見(jiàn)方案
路由表由前端去維護(hù)
也就是說(shuō)我們已知這幾個(gè)角色分別對(duì)應(yīng)哪些權(quán)限,前端寫(xiě)好路由配置表,,后端只需要告訴你當(dāng)前登錄人是屬于哪個(gè)角色,前端根據(jù)角色類(lèi)型去寫(xiě)一個(gè)過(guò)濾函數(shù),獲取該角色所擁有的菜單以及按鈕,然后動(dòng)態(tài)的去添加路由,這樣做有一個(gè)缺點(diǎn)就是,如果又增加了一個(gè)新的角色怎么辦?某個(gè)角色想增加一些權(quán)限怎么辦?這個(gè)時(shí)候前端就需要去新增或者修改代碼,一點(diǎn)也不友好
路由表的數(shù)據(jù)由后端返回
這種也是常用的一種方式,可能我們的系統(tǒng)里面需要開(kāi)發(fā)一些功能,如菜單管理,角色管理,用戶(hù)管理等,也就是常說(shuō)的權(quán)限中心,前端開(kāi)發(fā)完的頁(yè)面所對(duì)應(yīng)的路由信息有后端去維護(hù),這個(gè)時(shí)候我們?cè)陂_(kāi)發(fā)的時(shí)候需要約束某一種規(guī)則,比如所有頁(yè)面都放到/src/views目標(biāo)下面,然后通過(guò)菜單管理去維護(hù)數(shù)據(jù),維護(hù)完的數(shù)據(jù)可以到角色管理里面去配置角色菜單,配置完角色,可以到用戶(hù)管理里面給某個(gè)用戶(hù)配置角色等一些列流程,這樣的話(huà)即使新增一個(gè)角色,或者修改一個(gè)角色的角色菜單,前端都不需要去修改代碼,只要在菜單管理里面維護(hù)好了數(shù)據(jù),想怎么改怎么改,后端返回的數(shù)據(jù)格式可能如下:
[
{
path: "/application",
name: "application",
component: "Layout",
title: "應(yīng)用管理",
show: true,
icon: "",
children: [
{
path: "",
name: "application-index",
component: "/application/index.vue",
},
],
},
{
path: "/permission",
name: "permission",
component: "Layout",
title: "權(quán)限管理",
show: true,
icon: "",
children: [
{
path: "menu",
name: "permission-menu",
component: "/permission/menu/index.vue",
title: "菜單管理",
show: true,
icon: "",
},
{
path: "user",
name: "permission-user",
component: "/permission/user/index.vue",
title: "用戶(hù)管理",
show: true,
icon: "",
},
{
path: "role",
name: "permission-role",
component: "/permission/role/index.vue",
title: "角色管理",
show: true,
icon: "",
},
],
},
]
其實(shí)仔細(xì)觀察這個(gè)數(shù)據(jù)結(jié)構(gòu),是不是有點(diǎn)熟悉,path,name,component,children,我們好像手動(dòng)維護(hù)路由表的時(shí)候也會(huì)用到這些屬性
如何實(shí)現(xiàn)動(dòng)態(tài)路由
實(shí)現(xiàn)動(dòng)態(tài)路由其實(shí)就要用到vue-router提供的一個(gè)方法,叫addRoute在之前版本的時(shí)候是addRoutes,現(xiàn)在非版本的vue-router給廢除了addRoute()如何使用呢?可以看一下官方文檔
當(dāng)我們添加一個(gè)主路由的時(shí)候
router.addRoute({ path: '/permission', name: 'permission', component: () => import('xxxxx')})
添加子路由也就是嵌套路由
router.addRoute('主路由的name', { path: 'settings', component: AdminSettings })

既然我們已經(jīng)知道了addRoute()方法如何使用,下面我們就可以去實(shí)現(xiàn)這部分邏輯
我們看一下官方文檔的導(dǎo)航守衛(wèi)里面的內(nèi)容


在之前我們使用導(dǎo)航守衛(wèi)的時(shí)候需要一個(gè)參數(shù)next()去控制是否放行以及去哪個(gè)頁(yè)面,但是在新版本的vue-router里面可以不是用next(),當(dāng)然你是用也行~
我們可以新建一個(gè)permission.ts文件
import router from "./index";
import { useSessionStorage } from "@vueuse/core";
import { StorageEnum } from "@/enum";
import { useUserStore } from "@/store";
const whiteList = ["/login", "/404"];
router.beforeEach(async (to) => {
const token = useSessionStorage(StorageEnum.TOKEN, "").value;
// 如果在白名單里面 并且 token 不存在
if (whiteList.includes(to.path) && !token) {
return true;
} else {
const { menuList, getMenuList } = useUserStore();
// 如果為空數(shù)組,name就請(qǐng)求接口重新獲取后端維護(hù)的路由數(shù)據(jù)
if (!menuList.length) {
await getMenuList();
console.log("獲取全部路由 =>", router.getRoutes());
// 觸發(fā)重定向 不這樣寫(xiě)會(huì)導(dǎo)致刷新找不到路由 兩種寫(xiě)法都行
// return { path: to.fullPath };
return to.fullPath;
}
}
});
這里我們可以看到一會(huì)return true一會(huì)return to.fullpath是為什么,通過(guò)官方導(dǎo)航守衛(wèi)里面的介紹,我們可以知道的是

通過(guò)官方文檔動(dòng)態(tài)路由,我們可以直到

所以說(shuō)return to.fullpath是官方告訴我們要這么使用,也是為了解決動(dòng)態(tài)路由頁(yè)面刷新的時(shí)候會(huì)出現(xiàn)頁(yè)面空白或者404的問(wèn)題
出現(xiàn)404的話(huà)比如說(shuō)你在路由表中維護(hù)了下面路由映射
{
path: "/:pathMatch(.*)",
name: "page404",
component: () => import("@/views/system/404.vue"),
}
上面說(shuō)的主要是在全局導(dǎo)航守衛(wèi)里面的一些使用及注意事項(xiàng),我們可以看到并沒(méi)有用到addRoute()這個(gè)方法,也沒(méi)有出現(xiàn)拿到后端數(shù)據(jù)前端轉(zhuǎn)換成路由表的相關(guān)代碼,但是我們可以看到有一個(gè)getMenuList()函數(shù),我們?cè)陂_(kāi)發(fā)的時(shí)候,肯定是要考慮到復(fù)用以及復(fù)雜邏輯抽取的問(wèn)題,一個(gè)動(dòng)態(tài)路由的實(shí)現(xiàn)會(huì)牽扯到很多知識(shí)點(diǎn)接口請(qǐng)求,vuex或者pinia狀態(tài)維護(hù),vue-router,vite里面怎么獲取文件等下面看一下如何獲取到接口給的數(shù)據(jù),轉(zhuǎn)換成vue-router能夠識(shí)別的數(shù)據(jù)首先我們要看一個(gè)vite官方給提供的功能,我們拿到后端給的文件路徑如上面代碼出現(xiàn)的component字段,如/application/index.vue,這個(gè)文件可能在我們本地項(xiàng)目中的src/views/application/index.vue這個(gè)路徑下

我們需要把它轉(zhuǎn)換成一個(gè)下面的格式() => import('xxxx'),我們需要?jiǎng)討B(tài)的去拼接獲取文件,該怎么實(shí)現(xiàn)呢?
vite官方文檔中有說(shuō)明

需要使用import.meta.glob,這個(gè)時(shí)候我們可以打印一下import.meta.glob("../views/**"),看一下返回的到底是什么

返回的是一個(gè)對(duì)象,而對(duì)象的key我們可以拼接獲取到,而value正是我們想要的動(dòng)態(tài)獲取的文件路徑
以下代碼僅供參考,有很多需要完善的地方,只為演示使用
import type { RouterType } from "@/router/type";
import router from "@/router";
import type { RouteRecordRaw } from "vue-router";
export const useRouterConfig = () => {
// 獲取views目錄下的所有的文件 不要使用@別名
const modules = import.meta.glob("../views/**");
const asyncRoutes = ref<RouterType[]>([]);
const addRoutes = (menus: RouterType[]) => {
asyncRoutes.value = menus;
filterAsyncRouter();
// 動(dòng)態(tài)添加 / 路由
router.addRoute({
path: "/",
redirect: asyncRoutes.value[0].path,
});
};
const filterAsyncRouter = () => {
const routerLoop = (routes: RouterType[], ParentName?: string) => {
routes.forEach((item) => {
if (item.component === "Layout") {
item.component = () => import("@/layout/index.vue");
} else {
item.component = resolveComponent(item.component);
}
const { title, show, icon, name, path, component, children } = item;
const route: RouteRecordRaw = {
component,
path,
name,
meta: {
title,
show,
icon,
},
children: children as any,
};
// 動(dòng)態(tài)添加路由
if (ParentName) {
router.addRoute(ParentName, route);
} else {
router.addRoute(route);
}
if (item.children && item.children.length > 0) {
routerLoop(item.children, item.name);
}
});
};
routerLoop(asyncRoutes.value);
};
const resolveComponent = (path: string) => {
console.log(modules);
// 拿到views下面的所有文件之后,動(dòng)態(tài)拼接`key`去獲取value
const importPage = modules[`../views${path}`];
if (!importPage) {
throw new Error(
`Unknown page ${path}. Is it located under Pages with a .vue extension?`
);
}
return importPage;
};
return { addRoutes };
};
綜上所述:
要想實(shí)現(xiàn)vite+vue-router實(shí)現(xiàn)動(dòng)態(tài)路由我們需要用到
addRoute()import.meta.glob()獲取后端tree數(shù)據(jù),遞歸循環(huán),可能業(yè)務(wù)會(huì)有type類(lèi)型,比如分為模塊,菜單,頁(yè)面,按鈕等,到時(shí)候結(jié)合業(yè)務(wù)去實(shí)現(xiàn)邏輯導(dǎo)航守衛(wèi)使用時(shí)的注意事項(xiàng),否則會(huì)出現(xiàn)刷新白屏,或者路由訪問(wèn)死循環(huán)等
總結(jié)
到此這篇關(guān)于vue3+vue-router+vite實(shí)現(xiàn)動(dòng)態(tài)路由的文章就介紹到這了,更多相關(guān)vue-router+vite動(dòng)態(tài)路由內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
vue2項(xiàng)目導(dǎo)出操作實(shí)現(xiàn)方法(后端接口導(dǎo)出、前端直接做導(dǎo)出)
這篇文章主要給大家介紹了關(guān)于vue2項(xiàng)目導(dǎo)出操作實(shí)現(xiàn)方法的相關(guān)資料,文中介紹的是后端接口導(dǎo)出、前端直接做導(dǎo)出,通過(guò)代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2024-05-05
vue 實(shí)現(xiàn)LED數(shù)字時(shí)鐘效果(開(kāi)箱即用)
這篇文章主要介紹了vue 實(shí)現(xiàn)LED數(shù)字時(shí)鐘效果(開(kāi)箱即用),每一個(gè)數(shù)字由七個(gè)元素構(gòu)成,即每一個(gè)segment元素,本文給大家分享實(shí)現(xiàn)實(shí)例,感興趣的朋友一起看看吧2019-12-12
基于vue展開(kāi)收起動(dòng)畫(huà)的示例代碼
這篇文章主要介紹了基于vue展開(kāi)收起動(dòng)畫(huà)的示例代碼,小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2018-07-07
vue導(dǎo)入excel文件,vue導(dǎo)入多個(gè)sheets的方式
這篇文章主要介紹了vue導(dǎo)入excel文件,vue導(dǎo)入多個(gè)sheets的方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-08-08
vue中formdata傳值給后臺(tái)時(shí)參數(shù)為空的問(wèn)題
這篇文章主要介紹了vue中formdata傳值給后臺(tái)時(shí)參數(shù)為空的問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-06-06
vue使用WEB自帶TTS實(shí)現(xiàn)語(yǔ)音文字互轉(zhuǎn)的操作方法
這篇文章主要介紹了vue使用WEB自帶TTS實(shí)現(xiàn)語(yǔ)音文字互轉(zhuǎn),本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友參考下吧2024-01-01

