发现问题
在vue后台管理项目中,动态路由是一个必须的功能,根据不同权限显示不同的路由表。通常的做法有两种,1)前端将所有路由定义到配置文件中,后台返回用户角色。前端根据用户角色从配置文件中去相应的路由对象生成路由表。(此种方法用的比较多,至今未发现有何问题). 2)前端路由表中只配置公共路由,后台根据前端登录用户返回相应的路由表。前端拿到路由表动态(addRoutes)的加载出来。正常登录没啥问题,登录成功后页面跳转也可以,但是在动态添加的路由部分刷新页面会出现404问题。
router.beforeEach((to, from, next) => {
if (from.query.code || to.query.code ) {
let time = (new Date()).valueOf()
localStorage.setItem('EXPIRE-TIME', time)
}
isLogin(to, from, next)
})
router.afterEach(() => {})
//判断是否需要跳转到登录页
function isLogin(to, from, next) {
if (JSON.parse(localStorage.getItem('IS_LOGIN'))) { ......
} else if (!JSON.parse(localStorage.getItem('IS_LOGIN'))) {
// 在免登录白名单,直接进入
if (whiteList.indexOf(to.path) !== -1 || to.path.search(/(^\/i\/[a-z0-9A-Z]+$)/g) > -1) {
next()
} else {
if(to.path == '/'){ queryUserInfo(next)
}else{ next('/user/login')
}
} }}
//在登录之后,进入首页之前进行信息获取,与是否登录验证
function queryUserInfo(next) {
let url = `${process.env.VUE_APP_URL}/userprofile`
axios.get(url).then((res) => {
if (res.data.success) {
......
//初始化页面数据
if(routeTable && routeTable.length > 0){
addRoutes(routeTable)
next()
}else{
//从后台获取路由表
localStorage.setItem('ROUTE_TABLE',"[]")
let url = `${process.env.VUE_APP_URL}/api/permission/menu`
axios.get(url).then(res=>{
if(res.data.success){
routeTable = res.data.data
}
localStorage.setItem('ROUTE_TABLE',JSON.stringify(res.data.data))
addRoutes(routeTable)
next()
})
}
} else {
next('/user/login')
}
}).
catch(error=>{
next('/user/login')
})}
// 在登录之后,进入首页之前进行路由表初始化
function initRouters(routeTable) {
//遍历后台传来的路由字符串,转换为组件对象
let newRoutes = []
routeTable.forEach(route => {
let oRouter = {}
if (route.component) {
oRouter = {
path: route.path,
component(resolve) {
.....//将后台返回的字符串转化为对应的组件对象
},
....//路由相关信息
hidden: route.hidden ? route.hidden : false,
children: route.children && route.children.length > 0 ? initRouters(route.children) : []
}
if( oRouter.children.length == 0){
delete oRouter.children
}
}
newRoutes.push(oRouter)
})
return newRoutes
}
//增加新路由到路由表
function addRoutes(routeTable){
let newRoutes = []
let oldRoutes = routers
routers.forEach(item=>{
let obj = {}
......//生成新的路由表
if(item.path == '/'){
obj.children = item.children.concat(initRouters(routeTable))
}
newRoutes.push(obj)
})
Store.commit('SET_ROUTERS',newRoutes)
router.addRoutes(newRoutes)}
复制代码
2.问题分析
问题是页面刷新导致的,那我们就来分析下vue中页面刷新对于路由来说所走的流程。
vue加载main.js=>main.js调用router=>获取浏览器URL=>router根据路由表找对应的组件=>找到对应的组件,加载组件(在加载组件前router.beforEach())=>显示页面
理清楚上面的路由加载流程则不难看出问题出在router根据路由表找对应的组件(此时后台返回的路由数据还未加载到路由表中)
3.解决思路
既然加载顺序问题,那我们就需要一个能将后台返回的路由数据存储的功能(自然而然想到 localstorage)。
1)配置公共路由
2)首次登录成功将后台返回的路由数据存在localstorage中并且动态(addRoutes)将数据添加到路由表数组中
3)在公共路由router.js中判断本地localstorage的ROUTER_TABLE是否存在,如果不存在则正常加载,如果存在则取出localstorage中的ROUTER_TABLE并处理放入router.js的数组中
4)全局导航监听(router.beforEach(function(to,from,next){}),判断localstorage中的路由数据是否存在,如果存在处理后放入动态路由中(addRoutes),否则请求后台数据获取相关数据
4.相关代码
1)定义router.js配置公共路由
let routers=[
..........
]
export default routers
2)vue router全局导航钩子监听路由加载在进入动态路由对应的页面前使用addRoutes添加路由
router.berforEach(function(to,from,next){
.....请求后台,待数据返回
axios.get(url).then(res=>{
if(res.data.success){
localstorage.setItem("ROUTER_TABLE")
router.addRoutes(....)
}
}
})
3)处理路由表router.js
let routeTable =localStorage.getItem("ROUTE_TABLE") ? JSON.parse(localStorage.getItem("ROUTE_TABLE")) : []
if(routeTable && routeTable.length > 0){
routers = addRoutes(routers)
}
function initRouters(routeTable) {
//遍历后台传来的路由字符串,转换为组件对象
let newRoutes = []
routeTable.forEach(route => {
let oRouter = {}
if (route.component) {
oRouter = {
path: route.path,
component(resolve) {
....
}
newRoutes.push(oRouter)
})
return newRoutes
}
//增加新路由到路由表
function addRoutes(routers){
let newRoutes = []
routers.forEach(item=>{
let obj = {}
for(let key in item){
obj[key] = item[key]
}
if(item.path == '/'){
obj.children = item.children.concat(initRouters(routeTable))
} newRoutes.push(obj) })
Store.commit('SET_ROUTERS',newRoutes)
return newRoutes
}
4)最请参照第一段相关代码
复制代码