一、 问题代码
最近在实现前端动态路由时遇到一个问题,当我登录跳转到home页面时,页面空白。我的动态路由实现思路是封装一个initMenu函数,该函数通过请求后端菜单数据,对数据进行处理,将数据路由加入到router中,并将数据保存在vuex中方便后续使用。
initMenu代码:
import {getRequest} from "./api";
export const initMenu = (router, store) => {
return new Promise((resolve, reject) => {
getRequest("/menu").then(res => {
if (res && res.code === 200) {
let fmtRoutes = formatRoutes(res.data);
router.addRoutes(fmtRoutes);
store.commit('initRoutes', fmtRoutes);
resolve();
} else {
reject("菜单数据获取失败");
}
}).catch(err => {
reject(err);
});
});
}
export const formatRoutes = (routes) => {
let fmRoutes = [];
routes.forEach(router => {
let {
path,
component,
name,
meta,
iconCls,
children,
} = router;
if (children && children instanceof Array) {
//递归
children = formatRoutes(children);
}
let fmRouter = {
path: path,
name: name,
meta: meta,
iconCls: iconCls,
children: children,
component(resolve) {
if (component.startsWith("Home")){
require(['../views/' + component + 'Vue.vue'], resolve);
}else if (component.startsWith("Emp")) {
require(['../views/emp/' + component + '.vue'], resolve);
} else if (component.startsWith("Per")) {
require(['../views/per/' + component + '.vue'], resolve);
} else if (component.startsWith("Sal")) {
require(['../views/sal/' + component + '.vue'], resolve);
} else if (component.startsWith("Sta")) {
require(['../views/sta/' + component + '.vue'], resolve);
} else if (component.startsWith("Sys")) {
require(['../views/sys/' + component + '.vue'], resolve);
}
}
}
fmRoutes.push(fmRouter);
})
return fmRoutes;
}
路由守卫代码:
router.beforeEach( async (to, from, next) => {
if (to.path === '/login') {
next()
}
if (sessionStorage.getItem("token")) {
//用户已登录获取用户信息
const res = await getRequest("/admin/info")
if (res) {
sessionStorage.setItem('user', JSON.stringify(res.data))
}
if (store.state.routes.length > 0) {
next()
} else {
//获取菜单数据
await initMenu(router, store)
next()
}
} else {
//若用户未登录跳回登录页面
next("/login")
}
})
二、问题描述
进入子路由页面后,刷新会出现空白,我在网上查了许多解决方法,基本意思是说路由在还未加载完成就进行了路由跳转,但是我在路由守卫获取菜单数据的方法前加了await,理论上来说,在进入到新路由前,我的菜单数据应该是准备好的。
三、问题原因与解决方法
首先问题原因确实是路由还未加载完成就进行了路由跳转,至于为什么使用了await没有解决问题,原因是即使使用await等待菜单数据加载上了,但是Vue Router使用的是旧的路由表,旧的路由表是没有动态路由的数据的,相当于路由跳转到一个不存在的路由上,从而页面白屏。
解决方法是在获取动态路由后使用next({...to,replace:true})进行放行,该代码的意思是触发一次新的导航到当前正在访问的地址,进行新的导航时,路由数据已经准备就绪,页面就可以正常的展示了。
Vue Router导航流:
导航开始时->根据当时的路由表做一次匹配->匹配到组件后,进入渲染流程
导航中途即使修改了路由表,当前导航不会自动重新匹配,必须重新发起一次导航,让Vue Router走一遍完整的匹配。
具体例子:
页面刷新相当于Vue Router开车通过地图从A地前往A地,开车到一半发现没有地图上没有A地,于是迷路(页面空白),此时即使更新完地图(补充动态路由),但是车已经在迷路状态,Vue Router不会自己掉头用新地图再去一次A地,此时需要使用next({...to, replace:true}让Vue Router再次出发前往A地,第二次Vue Router就使用最新的地图(更新完后的路由表)。
修改后的路由守卫代码:
router.beforeEach( async (to, from, next) => {
const token = sessionStorage.getItem("token");
if (to.path === '/login') {
next();
} else if (token) {
if (!sessionStorage.getItem('user')) {
const res = await getRequest('/admin/info');
if (res) {
sessionStorage.setItem('user', JSON.stringify(res.data));
}
}
if (store.state.routes.length === 0) {
await initMenu(router, store);
next({ ...to, replace: true });
} else {
next();
}
} else {
next('/login');
}
});