背景
/home:項目首頁,同步路由
/uc/profile:個人資訊頁,異步路由,登入後根據使用者角色展示。
/login?redirect=%2Fuc%2Fprofile(%2F是 / 的url轉義) :登入頁,同步路由,在個人資訊頁登出會進入登入頁并攜帶redirect=/uc/profile,再次登入成功後,直接進入個人資訊頁
項目:基于vue-element-admin,對權限菜單,異步路由,Mock檔案都已經寫好了。隻需要根據需求修改就可以!
問題:
最開始項目沒有配置重定向404(路由沒有 {path:"*",redirect:"/"} ),在/uc/profile頁面重新整理可以重新顯示,并且登出後重新登入也可以再次顯示頁面。
此時路由配置:
// 同步路由
export const constantRoutes = [
...homeRouter,
{
path: "/404",
component: () => import("@/views/error-page/404"),
hidden: true
}
];
// 異步路由(通過後端擷取或者前端篩選添加)
export const asyncRoutes = [
{
path: "/uc",
component: Layout,
children: [
{
path: "profile",
component: () => import("@/views/manage/profile"),
name: "Profile",
meta: { title: "個人資訊", icon: "el-icon-user" }
},
{ path: "*", redirect: "/404", hidden: true }
]
}
];
// beforeEach
router.beforeEach(async (to, from, next) => {
const hasToken = getToken();
if (hasToken) {
if (to.path === "/login") {
next();
} else {
const hasRoles = store.getters.roles && store.getters.roles.length > 0;
if (hasRoles) {
next();
} else {
try {
const { roles } = await store.dispatch("user/getInfo");
const accessRoutes = await store.dispatch(
"permission/generateRoutes",
roles
);
router.addRoutes(accessRoutes);
next({ ...to, replace: true });
} catch (error) {
await store.dispatch("user/resetToken");
next(`/login?redirect=${to.path}`);
}
}
}
} else {
if (whiteList.indexOf(to.path) !== -1) {
next();
} else {
next(`/login?redirect=${to.path}`);
}
}
});
效果:
存在的問題:輸入錯誤的路徑,例如:/home1,隻會顯示白屏。
為了處理錯誤的路徑,給路由添加重定向;這是輸入/home1可以重定向到404頁了;
export const constantRoutes = [
...homeRouter,
{
path: "/404",
component: () => import("@/views/error-page/404"),
hidden: true
},
{ path: "*", redirect: "/404" } // 添加
];
效果:
存在的問題:錯誤路徑可以重定向到404了,但是異步路由頁面重新整理會重定向到404,并且在異步路由頁面登出,再登入後自動重定向到異步路由的頁面也會404;
這時的from和to如下,from變成了null,to是404
其他解決方案:
網上看了不少部落格,基本都是使用sessionStorage去在監聽unload事件,在重新整理的時候去報存異步頁面的路徑;在beforeEach中addRoute之後,判斷sessionStorage,去跳轉到異步頁面;
// App.vue
watch: {
$route: () => {
window.addEventListener("beforeunload", () => { // unload也一樣
sessionStorage.setItem("tp", window.location.pathname);
});
},
},
-----------------------
// beforeEach
.....
router.addRoutes(accessRoutes);
let p = sessionStorage.getItem("tp");
if (p.startsWith('/uc')) { // 判斷是否為異步路由
sessionStorage.removeItem("tp");
router.replace(p);
} else {
sessionStorage.removeItem("tp");
next({ ...to });
}
....
這樣解決異步路由的問題,但是又産生了其他問題。
你需要在擷取到sessionStorage之後去判斷這個路徑是不是異步的(如果不判斷的話,你在頁面通過a的href是無法跳轉的,并且你在/home頁下故意輸入錯誤路徑/home1,并不會重定向到404,會重新回到/home頁面等許多問題)。
最終方案
beforeEach的to參數,有一個redirectedFrom屬性,可以根據這個屬性來進行判定。
// beforeEach
...
router.addRoutes(accessRoutes);
if (to.redirectedFrom) {
router.replace(to.redirectedFrom);
} else {
next({ ...to });
}
...
這個方式,不需要監聽unload事件,不需要判定路徑是否為異步