本來很簡單的一件事,給背景差點弄得當場去世。
前言:動态路由就是根據一個json格式的數組來生成Vue中的路由表,實作動态注冊。可以前端自己實作,也可以使用背景傳回的路由表來生成。
坑1-----------------------------前背景字段不比對
坑2-----------------------------轉換使用異步思想,如果沒有添加路由資訊便開始跳轉,則會出現頁面跳轉空白和404
說起這個,弄得自己差點就懷疑人生(唉,忍住忍住,不是背景的鍋),先說明一下,路由裡面,最基礎的字段有path,name,component,meta。其中path為跳轉時浏覽器顯示的路徑,name為辨別,component為該path對應的元件路徑,meta為路由攜帶的資訊,裡面可以定義任意的字段。使用的最重要的方法為addRoutes()。
要實作一個動态路由有如下幾個步驟:
1.準備一個路由表
2.将路由表轉換為自己想要的資料格式
3.使用addRoutes添加
由于添加路由addRoutes是一個異步的方法,是以可以配個async和await來使用,效果更好。
1.準備路由表:
leftMenu: [{
"title": "工作台",
"path": "/home",
"icon": "el-icon-house",
"children": [],
id: 1
}, {
"title": "借貸管理",
"path": "/debitManage",
"icon": "el-icon-money",
id: 1,
"children": [{
"title": "送出借款初審",
"path": "/debitManage/firstCon",
id: 1,
"children": [{
"title": "新增借款",
"path": "/debitManage/addDebitItem",
name: "addDebitItem",
id: 1,
"children": []
}, {
"title": "新标維護",
"path": "/debitManage/upholdNewBidItems",
name: "addDebitItem",
id: 1,
"children": []
}, {
"title": "借款稽核",
"path": "/debitManage/firstDebitCon",
name: "addDebitItem",
id: 1,
"children": []
}]
}, {
"title": "上架管理",
id: 1,
"path": "/debitManage/secondCon",
"children": [{
"title": "上架初審",
"path": "/debitManage/firstLevelAuditItems",
name: "addDebitItem",
id: 1,
"children": []
}, {
"title": "标的上架",
"path": "/debitManage/secondBidsGroundItems",
name: "addDebitItem",
id: 1,
"children": [
]
}, {
"title": "進行中的标管理",
"path": "/debitManage/runningBidsItems",
name: "addDebitItem",
id: 1,
"children": []
}]
},
{
"title": "複審管理",
"path": "/debitManage/thridCon",
id: 1,
"children": [{
"title": "滿标複審",
"path": "/debitManage/reexAuditItems",
name: "addDebitItem",
id: 1,
"children": []
}]
}, {
"title": "記錄檢視",
"path": "/debitManage/viewLogs",
id: 1,
"children": [{
"title": "所有借款标",
"path": "/debitManage/debitItems",
name: "addDebitItem",
id: 1,
"children": []
}, {
"title": "投資記錄",
"path": "/debitManage/investRecords",
name: "addDebitItem",
id: 1,
"children": []
}]
}, {
"title": "管理标類别",
"path": "/debitManage/manageCategory",
id: 1,
"children": [{
"title": "借款标類别",
"path": "/debitManage/debitCategory",
name: "addDebitItem",
id: 1,
"children": []
}]
}
]
}, {
"title": "還款管理",
"path": "/repaymentManage",
"icon": "el-icon-bank-card",
id: 1,
"children": [{
"title": "管理還款項",
"path": "/repaymentManage/repaymentItems",
name: "addDebitItem",
id: 1,
"children": []
},
{
"title": "還款記錄",
"path": "/repaymentManage/repaymentRecords",
name: "addDebitItem",
"children": [],
id: 1
}
]
}, {
"title": "資金管理",
"path": "/capitalManage",
id: 1,
"icon": "el-icon-wallet",
"children": [{
"title": "記錄檢視",
"path": "/capitalManage/logsView",
id: 1,
"children": [{
"title": "充值記錄",
"path": "/capitalManage/reChargeLog",
id: 1,
name: "addDebitItem",
"children": []
}, {
"title": "提現記錄",
"path": "/capitalManage/withDrawMoneyLog",
id: 1,
name: "addDebitItem",
"children": []
}, {
"title": "使用者資金",
"path": "/capitalManage/userCapital",
name: "addDebitItem",
id: 1,
"children": []
}, {
"title": "平台資金",
"path": "/capitalManage/platformCapitalLog",
name: "addDebitItem",
id: 1,
"children": []
}]
}, {
"title": "相關稽核",
"path": "/capitalManage/withDrawConfirm",
id: 1,
"children": [{
"title": "提現稽核",
"path": "/capitalManage/withDrawCon",
id: 1,
name: "addDebitItem",
"children": []
}]
}]
}, {
"title": "會員管理",
"path": "/memberManage",
"icon": "el-icon-user-solid",
id: 1,
"children": [{
"title": "新增借款使用者",
"path": "/memberManage/addDebitUser",
name: "addDebitItem",
id: 1,
"children": []
},
{
"title": "投資使用者管理",
"path": "/memberManage/investUserManage",
name: "addDebitItem",
id: 1,
"children": []
},
{
"title": "借款使用者管理",
"path": "/memberManage/debitUserManage",
name: "addDebitItem",
id: 1,
"children": []
}
]
}, {
"title": "内容管理",
"path": "/contentManage",
"icon": "el-icon-s-grid",
id: 1,
"children": [{
"title": "輪播管理",
"path": "/contentManage/sliderManage",
id: 1,
"children": [{
"title": "新增輪播",
"path": "/contentManage/sliderManage/addSliderItem",
"children": [],
id: 1
}]
},
{
"title": "文章管理",
"path": "/contentManage/articleManage",
id: 1,
"children": [{
"title": "新增文章",
"path": "/contentManage/articleManage/addArticleItem",
"children": [],
id: 1,
}]
},
{
"title": "意見回報",
"path": "/contentManage/feedBack",
id: 1,
"children": [{
"title": "回報處理",
"path": "/contentManage/feedbackManage/addFeedbackItem",
"children": [],
id: 1,
}, ]
}
]
}]
分割線:
2.在cli搭建的項目的router檔案夾中替換index.js檔案,建立一個index.js檔案,在裡面寫預先定義好的靜态路由,并且寫上初始化路由的方法
router/index.js
import Vue from "vue";
import Router from "vue-router";
// import Layout from "@/Layout";
import Login from "@/views/Login";
Vue.use(Router);
const originalPush = Router.prototype.push;
Router.prototype.push = function push(location) {
return originalPush.call(this, location).catch(err => err);
};
export const constRouters = [
{
path: "/login",
component: Login,
hidden: true,
name: "login"
}
];
const createRouter = () =>
new Router({
// mode: "history",
routes: constRouters
});
const index = createRouter();
export function resetRouter() {
const newRouter = createRouter();
index.matcher = newRouter.matcher;
}
export const errorRoute = [
{
path: "*",
component: () => import("@/views/NotFound"),
hidden: true,
name: "pageNotFound",
meta: { title: "頁面不見啦" }
},
{
name: "login",
path: "/login",
component: () => import("../views/Login")
}
];
export default index;
3.建立一個轉化路由格式的檔案:asyncRouter.js
//引入公共路由
export function getAsyncRoutes(routes) {
const res = [];
const keys = ["path", "name", "children"];
if (Array.isArray(routes)) {
routes.forEach(v => {
const newItem = {};
if (v.path) {
//如果m有children,代表它是二級或者n級菜單,不引入元件
if (v.children && v.children.length > 0) {
newItem.component = () => "";
} else if (v.path === "/") {
newItem.component = newItem.component = resolve => {
require([`@/views${v.component}`], resolve);
};
} else {
//如果不是,就替換
newItem.component = resolve => {
require([`@/views${v.path}/index`], resolve);
};
}
}
for (const key in v) {
if (keys.includes(key)) {
//由于沒有name字段不合規範,隻能替換name字段為path後面的一截
if (key === "name") {
newItem["name"] = v["path"].split("/")[
v["path"].split("/").length - 1
];
} else {
newItem[key] = v[key];
//生成标題字段
newItem.meta = { title: v.name };
}
// } else {
// newItem["name"] = v["path"].split("/")[
// v["path"].split("/").length - 1
// ]+v.id;
// }
}
}
if (v.path) {
if (
(v.children == null || v.children.length === 0) &&
v.path !== "/home"
) {
newItem["name"] = v["path"].split("/")[
v["path"].split("/").length - 1
];
}
}
//如果有children,使用遞歸繼續調用該方法,實作任意深度都可以轉換
if (newItem.children && newItem.children.length) {
newItem.children = getAsyncRoutes(v.children);
}
res.push(newItem);
});
}
return res;
}
4.與index.js同級建立一個permission.js檔案,攔截并處理跳轉加載
import router from "../router";
import store from "../store";
import NProgress from "nprogress";
// console.log(store);
import "nprogress/nprogress.css";
import { resetRouter } from "../router";
import { getAsyncRoutes } from "@/router/asyncRouter";
import { errorRoute } from "../router";
const whileList = ["/login"];
var update = true;
router.beforeEach(async (to, from, next) => {
NProgress.start();
document.title = to.meta.title;
//擷取token
const hasToken = (await store.getters.getToken) !== "";
// console.log("token:" + hasToken);
if (hasToken) {
// console.log("有沒有token", hasToken);
if (to.path === "/login" || to.path === "/") {
next({ path: "/home" });
} else {
//擷取vuex中儲存的路由
let hasRoutes = await store.getters.getState;
//如果有路由則直接下一步,如果沒有則需要擷取路由
//判斷是不是點選重新整理
// console.log("是否有路由", hasRoutes);
if (hasRoutes && !update) {
next();
} else {
try {
await resetRouter();
const accessRoutes = getAsyncRoutes(await store.getters.getRoutes);
let changedRoutes = accessRoutes.filter(v => {
return v.path !== "/home/index";
});
//---------------------------------------------------===================================================
//-----------------------------------------------------================================================
//在這個下面添加自定義的路由,不要在index.js裡面添加
let extraRoutes = [
//在這裡寫額外的路由
{
path: "/sliderManage/sliderManage/addSlider",
name: "addSlider",
meta: { title: "新增輪播" },
component: () =>
import(
"../views/contentManage/sliderManage/sliderManage/addSlider"
)
},
{
path: "/articleManage/articleManage/addArticle",
name: "addArticle",
meta: { title: "新增文章" },
component: () =>
import(
"../views/contentManage/articleManage/articleManage/addArticle"
)
},
{
path: "/debitManage/Maintenance",
name: "Maintenance",
meta: { title: "上架複審" },
component: () =>
import("../views/debitManage/secondBidsGroundItems/Maintenance")
},
{
path: "/debitManage/RecheckAction",
name: "RecheckAction",
meta: { title: "複審" },
component: () =>
import("../views/debitManage/reexAuditItems/RecheckAction")
},
{
path: "/",
component: () => import("@/views/home/index"),
meta: { title: "工作台" },
name: "index"
},
{
path: "/Pwd",
name: "Pwd",
meta: { title: "密碼管理" },
component: () =>
import("@/components/rest/investUserManage/Pwd.vue")
},
{
path: "/luser3",
name: "luser3",
component: () =>
import("@/components/rest/debitUserManage/luser3")
},
{
path: "/Modify",
name: "Modify",
component: () =>
import("@/components/rest/investUserManage/Modify")
},
{
path: "/luser4",
name: "luser4",
component: () =>
import("@/components/rest/debitUserManage/luser4")
}
];
changedRoutes.push(...extraRoutes);
//console.log("轉換後的路由:", changedRoutes);
await router.addRoutes([
{
path: "/home",
component: () => import("@/Layout/Home"),
name: "home",
children: changedRoutes
}
]);
await store.commit("setNoRefresh");
update = false;
next({ ...to, replace: true });
} catch (error) {
next(`/login?redirect=${to.path}`);
console.log(error);
NProgress.done();
}
}
}
} else {
// console.log(to.path);
if (whileList.indexOf(to.path) !== -1) {
next();
} else {
next({ path: `/login` });
NProgress.done();
}
}
});
router.afterEach(async () => {
NProgress.done();
// console.log("導航結束");
});
檔案中定義了路由的全局前置守衛,當有token并且目前已經生成路由表時才放行;如果沒有路由表則要調用getAsyncRoutes方法;
注意:1.檔案中引入nprogress插件,頁面加載條,啟動使用start方法,停止使用done方法2.頁面重新整理時,路由資訊會被清空,需要重新添加3.使用一個狀态值來說明是否已經存在路由資訊4.addRoutes隻接受數組,最好使用解構然後指派;