1.发现问题
在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)最请参照第一段相关代码