一般來說,前端項目中的路由,很有可能是需要動态注冊的。因為菜單可能在管理系統中維護,還跟權限綁定,使用者登入以後,需要動态展示菜單。菜單往往跟路由挂鈎,是以,路由需要動态注冊。
具體如何實作呢?思路是,系統隻提供預設的路由,登入以後,讀入菜單/路由資料,加載。這其中,可能會出現預設路由與動态路由有重疊的情況。處理辦法是覆寫。資料結構方面,菜單與路由資料二合一。
一、項目結構
這是我們一個項目的公共架構的代碼結構。我們打算在src/modules/存放具體業務系統的代碼,而外部是相對穩定,可以複用的架構代碼。
二、預設路由
src/router/default.js,内容比較少,隻有登入、首頁、404三個。其餘全都靠動态注冊。
路由項中,自定義屬性放在meta裡。動态路由與預設路由的資料結構一緻。
/**
* 預設路由
* 具體業務路由,應在src/modules/router裡定義,或者從後端動态加載
*/
export default [
/**
* 路由項結構:
*{
path: "/",//路徑,(必選;path、name、component是路由規定必選的元素)
也可以帶參數,如 path: "/resource/detail/:id",
name: "路由名稱,是路由唯一辨別",(必選)
component: 指向元件,如 Home,或者() => import("../views/login/PageIndex.vue"),(必選)
meta: {//meta裡的屬性可以自定義,全部為可選項
text: "名稱",//展示在菜單裡,(可選)
navi: true, //導覽列(一級菜單),(可選)
noLogin: true, //無須登入即可浏覽,(可選)
access: "work:sysmanage,work:resourcemanage",//權限辨別,(可選)
},
children: [],
},
*/
{
path: "/login",
name: "login",
component: () => import("../views/login/PageIndex.vue"),
meta: {
noLogin: true, //無須登入即可浏覽
},
},
{
path: "/",
name: "Home",
component: () => import("../views/PageIndex.vue"),
},
{
path: "/notAllow",
name: "notAllow",
component: () => import("../views/sys/notAllow.vue"),
},
];
export const HomeName = "Home";
三、動态注冊
注冊的時機是什麼?在哪裡注冊?怎麼注冊?
答曰:登入之後注冊;在路由守衛裡注冊;用router.addRoute()一個個加進去。
1、登入之後進行注冊,在路由衛士裡注冊
所謂路由守衛,就是路由規則。這名字我是從網上抄過來的。
src/router/index.js
import { createRouter, createWebHashHistory } from "vue-router";
import routes from "./default";
import routeAssembler from "./setup";
import { hasAuthority } from "@/utils/login.js";
const router = createRouter({
history: createWebHashHistory(),
routes,
});
// 路由守衛
let registerRouteFresh = true;//是否還沒有動态加載過
router.beforeEach((to, from,) => {
const isLogin = localStorage.isLogin ? true : false;
if (isLogin) {
//已登入情況下驗證權限
let isAccess = hasAuthority(to.meta.access);
if (!isAccess) {//沒有權限
next("/notAllow");
return;
}
if (registerRouteFresh) {//還沒有動态加載過
//動态注冊路由 <----------------------------------------
routeAssembler(router);
registerRouteFresh = false;
next({ ...to, replace: true });
} else {
//已經登入了,不能再打開登入頁
to.path === "/login" ? next("home") : next();
}
} else {
//如果無須登入則直接打開,否則轉向登入頁面
to.meta.noLogin || to.path === "/login" ? next() : next("/login");
}
});
export default router;
2、如何動态注冊
src/router/setup.js,即上面例子中的routeAssembler:
/*
裝配路由及菜單
*/
import fixItems from "./default"; //預設路由
import { HomeName } from "./default"; //統一命名首頁路由項(參考前面的預設路由)
import projectItems from "@/modules/router"; //具體業務系統的路由
export default (router) => {
//獲得動态路由
const dynaItems = getDynamicItems();
//對齊首頁(統一命名首頁)
adpatHome(HomeName, dynaItems);
//添加動态路由
dynaItems.forEach((value) => {
router.addRoute(value);
});
};
const getDynamicItems = () => {
/*
擷取動态路由,從指定檔案加載或從後端擷取
*/
return projectItems;
};
//預設路由與業務路由對齊首頁的路由資訊
//所謂對齊,就是大家的name保持一緻,這樣才能保證動态加入的路由項,覆寫掉前面的path和name相同的路由
const adpatHome = (HomeName,) => {
let home = dynaItems.filter((item) => {
return item.path === "/";
});
if (home.length > 0 && home[0].name !== HomeName) {
/**
* 如果業務路由定義了首頁,但其name與預設路由首頁的name不相同
* 則将業務路由中首頁項的name置為預設名稱
* 因為按照vue-router的規則,addRoute的時候,如果存在同名同路徑的路由項,則覆寫之
* 我們要的就是覆寫預設,以業務路由設定為準
*/
home[0].name = HomeName;
}
};
這裡面有個如果動态路由,與預設路由中存在相同的路由項,該如何處理的問題。