之前写过一次文章是加载路由不切换页面页面空白的问题,这次又碰见了一次,而且比上次的解决方法好了一点点(一指甲盖那么大),所以写一篇记录下。
还是先说我解决问题后的结论:
- 不切换页面是退出后没有清除动态路由再次登录导致的。
- 页面空白即可以是没有清除动态路由导致的,也可能是登录时动态路由没有加载完成就跳到动态路由页面导致的。
- 出现404大概率是添加任意路由的位置在动态路由之前
我在代码块中加了汉字数字,与文章后边的汉字相对应。
此处解决的是跳转动态路由空白页的问题。
router.beforeEach(async (to, from, next) => {
NProgress.start();
if (userStore().token) {
try {
// 如果没有用户名进入此判断
一、 //+++++++++++++++++++++++++++++++
if (!userStore().userInfo?.name) {
// 获取用户信息,添加动态路由
await userStore().getUserInfo();
// 携带路径重新进入前置守卫,再次进入判断,再次进入时就已经获取到了用户名,且动态路由添加完毕,实现跳转
next({ ...to, replace: true });
//++++++++++++++++++++++++++
} else if (!whiteList.includes(to.path)) {
console.log(to.path);
next();
} else {
next("/");
}
} catch (err) {
ElMessage({
type: "error",
message: "token过期,请重新登录",
});
await userStore().logOut();
next({ path: "/login", query: { redirect: to.path } });
}
} else {
if (!whiteList.includes(to.path)) {
next("/login");
} else {
next();
}
}
});
一、此处是解决登录时动态路由没有加载完成就跳转到动态路由导致白页,判断有无用户信息来进行获取发送请求,获取用户信息。
注意,此处需要单独写成一个判断且next()里面要携带去往的地址,而不能直接写next(),我是在获取用户信息时直接就添加了动态路由,所以后边看似可以直接写next(),但是如果直接写next(),会导致路径中地址是正确的,但是页面时空白的。我看了好多文章,就是因为没有单独写成一个单独的判断,而是与第二个判断混在一块了,类似下面代码:
//错误代码示例:
if (!userStore().userInfo?.name) await userStore().getUserInfo();
if (!whiteList.includes(to.path)) {
next({ ...to, replace: true });
} else {
next("/");
}
这样就会形成死循环,因为next()里面加参数会再次进入全局前置守卫,而每一次都会true,就会陷入死循环,而next()内不加路径,又会导致空白页。
而我们的解决方案也正是利用了再次进入全局前置守卫的特性,当获取用户信息添加动态路由时就再一次进入全局前置守卫,此时动态路由就添加完成了。
我见过其他的解决方案,是在app实例创建之前就将动态路由添加进去,但总感觉不太通顺。
//获取用户信息:
async function getUserInfo() {
const res = await userInfoApi();
if (res.code == 200) {
userInfo.value = res.data;
二、 //++++++++++++++++++++++++++++++
// todo 此处不能使用json来进行深拷贝,因为json深拷贝无法对函数、元素拷贝,且要将任意路由添加到最后,不能放在静态路由当中,否则会优先匹配任意路由,导致404
let routes = [...filterAsyncRoutes(cloneDeep(asyncRoutes), res.data.routes), anyRoute];
//++++++++++++++++++++++++++
menuRoutes.value = [...constantRoutes, ...routes];
三、//+++++++++++++++++++++++++++++++++++
for (const item of routes) {
let removeRoute = router.addRoute(item);
// 将动态添加的路由的返回值保存起来,方便退出登陆时删除路由
removeRoutes.value.push(removeRoute);
}
//+++++++++++++++++++++++++++++++
return "ok";
} else {
return Promise.reject(new Error(res.message));
}
}
//退出登录
async function logOut() {
const res = await logoutApi();
if (res.code == 200) {
token.value = "";
removeItem("token");
userInfo.value = {};
四、//++++++++++++++++++++++
removeRoutes.value.forEach((removeRoute) => removeRoute());
//+++++++++++++++++++++
return "ok";
} else return Promise.reject(new Error(res.message));
}
二、此处需要对动态路由表进行深拷贝,因为需要对动态路由进行权限的过滤,如果直接使用原动态路由表则会对原动态路由表造成修改,使用不同权限的账号登录会造成路由缺失。
三、此处获取到用户信息后循环添加动态路由,此处vue3的addRoute()会有一个返回值,调用返回值就会删除路由,所以此处将返回值保存起来,push到一个数组内,方便使用。
四、退出登录时,循环调用保存起来的动态路由移除函数即可。如果没有保存,也可以使用vue-router提供的router.removeRoute(route.name)函数,只需要将循环将动态路由表的路由名传进去即可。
移除动态路由很重要,上篇文章讲过:
最大的bug在于。如果管理员登录后再退出,然后再使用一个权限很少的账号登录,虽然这个账号不会显示他没有的权限,但是如果他直接在浏览器地址栏内输入他没有权限的路径他也可以进去。除非刷新过。
而使用removeRoute()移除动态路由后,就没有此bug。