原先的代码:
--- main.js ---
import router from './router'
import store from './store'
router.beforeEach((to, from, next) => {
//如果存在token
if (window.sessionStorage.getItem("tokenStr")) {
//是不是要去登录页,登录了就不给去了
if (to.path == '/') {
next('/home')
} else {
//如果登录了并且不是去登录页,就获取一遍菜单
initMenu(router,store)
if (!window.sessionStorage.getItem('user')) {
//如果没有用户信息就获取
return getRequest('/system/config/info').then(resp => {
if (resp && resp.success != false) {
//存入用户信息
window.sessionStorage.setItem('user', JSON.stringify(resp))
next()
}
})
} else {
next();
}
}
} else {
//不存在token要去登录页就放行,不去登录页就强制进入登录页并带上重定向参数
if (to.path == '/') {
next();
} else {
next('/?redirect=' + to.path);
}
}
})
--- menu.js ---
if (store.state.routes.length > 0) {
next()
}
getRequest('/system/config/menu').then(data => {
// console.log(data)
if (data) {
let fmtRoutes = formatRoutes(data);
//添加到路由
console.log("添加菜单")
router.addRoutes(fmtRoutes)
store.commit("initRoutes", fmtRoutes);
}
})
export const formatRoutes = (routes) => {
let fmtRoutes = [];
routes.forEach(router => {
let {
path,
component,
name,
iconCls,
children,
} = router;
if (children && children instanceof Array) {
//递归
children = formatRoutes(children);
}
let fmtRouter = {
path: path,
name: name,
iconCls: iconCls,
children: children,
component(resolve) {
if (component.startsWith("Home")) {
require(['../views/' + component + '.vue'], resolve);
} else if (component.startsWith("Manage")) {
require(['../views/mng/' + component + '.vue'], resolve);
} else if (component.startsWith("Sys")) {
require(['../views/sys/' + component + '.vue'], resolve);
} else if (component.startsWith("Obj")) {
require(['../views/obj/' + component + '.vue'], resolve);
}
}
}
fmtRoutes.push(fmtRouter)
})
return fmtRoutes;
}
很显然我原先的方法是在路由导航守卫中进行条件判断然后调用获取动态菜单的方法,然而,经过调试,这种通过调用的方式,还没等菜单获取完,路由守卫就继续执行下去了,也就是异步执行,类似于ajax里面的嵌套,于是我们便得到了空白页。
所以,我们需要让动态菜单先加载完毕,我这边用的方法是把获取菜单的方法搬过来
修改后的代码:
--- main.js ---
router.beforeEach((to, from, next) => {
//如果存在token
if (window.sessionStorage.getItem("tokenStr")) {
//是不是要去登录页,登录了就不给去了
if (to.path == '/') {
next('/home')
} else {
//如果登录了并且不是去登录页,就获取一遍菜单
if (store.state.routes.length > 0) {
if (!window.sessionStorage.getItem('user')) {
//如果没有用户信息就获取
console.log("无用户信息")
return getRequest('/system/config/info').then(resp => {
if (resp && resp.success != false) {
//存入用户信息
window.sessionStorage.setItem('user', JSON.stringify(resp))
next()
return;
}
})
} else {
next();
return;
}
}
getRequest('/system/config/menu').then(data => {
// console.log(data)
if (data) {
//进行菜单的格式化操作
let fmtRoutes = formatRoutes(data);
//添加到路由
console.log("添加菜单")
router.addRoutes(fmtRoutes)
store.commit("initRoutes", fmtRoutes);
next({...to,replace:true})
}
})
}
} else {
//不存在token要去登录页就放行,不去登录页就强制进入登录页并带上重定向参数
if (to.path == '/') {
next();
} else {
next('/?redirect=' + to.path);
}
}
})
但即使你搬过来,还是阻止不了异步执行,于是,在搬过来之后还多写了这么一个东西
next({...to,replace:true})
(关于这句话的详细释义,我把参考文章放在下面链接了)
我的理解如下:意思是先暂时中断当前的路由守卫,带着…to的参数再去访问路由守卫,形成了一个嵌套,那第二遍执行的时候仍然会执行这句话,这不就无限嵌套了吗,所以为了防止无限嵌套,必须要有一个终止条件,那我的终止条件就是当我的菜单添加成功并添加到vuex中之后,然后再一层一层结束最后回到最外层,再顺序执行,这样就可以保证菜单先加载完了。
--- 终止条件 ---
if (store.state.routes.length > 0) {
next()
return
}
本文参考:https://blog.csdn.net/qq_41912398/article/details/109231418