vue-router添加动态路由及无限刷新白屏和添加不生效问题_router,高并发系统基础篇

76 阅读4分钟

const createRouter = () => new Router({ scrollBehavior: () => ({ y: 0 }), routes: constantRoutes })

const router = createRouter()

export function resetRouter() { const newRouter = createRouter() router.matcher = newRouter.matcher // reset router }

export default router


* store/modules/permission.js




> 

> 在store/modules下添加permission.js

> 

> 

> 




// 首先,从index.js中引入已经定义过的2个router数组 import { asyncRoutes, constantRoutes } from '@/router'

// 全局变量state,routes和addRoutes数组 const state = { routes: [], addRoutes: [] }

// mutations 是唯一可以更改state的函数,使用SET_ROUTES定义更改方法,SET_ROUTES(state, routes)的入参routes赋值给addRoutes数组,将constantRoutes静态路由数组增加routes;

const mutations = { SET_ROUTES: (state, routes) => { state.addRoutes = routes state.routes = constantRoutes.concat(routes) } }

// vue中store状态管理,通过actions调用 mutations 中封装的方法来实现对于state更改, // 这里是vue-element-admin中动态路由的主要判断逻辑发生地方,首先判定用户角色是否包含admin(可能多角色),是则将所有asyncRoutes 加入到constantRoutes,若用户角色没有包含admin,则调用filterAsyncRoutes函数,递归地判定asyncRoutes.roles属性是否有该角色,即是否有权限,将有权限的router赋值accessedRoutes 后加入到constantRoutes;

const actions = { generateRoutes({ commit }, roles) { return new Promise(resolve => { let accessedRoutes if (roles.includes('system')) { accessedRoutes = asyncRoutes || [] } else { accessedRoutes = filterAsyncRoutes(asyncRoutes, roles) } commit('SET_ROUTES', accessedRoutes) resolve(accessedRoutes) }) } }

//----------------- // 两个上面使用的方法 function hasPermission(roles, route) { if (route.meta && route.meta.roles) { return roles.some(role => route.meta.roles.includes(role)) } else { return true } }

export function filterAsyncRoutes(routes, roles) { const res = []

routes.forEach(route => { const tmp = { ...route } if (hasPermission(roles, tmp)) { if (tmp.children) { tmp.children = filterAsyncRoutes(tmp.children, roles) } res.push(tmp) } })

return res }

export default { namespaced: true, state, mutations, actions }


* src/permission.js




> 

> 修改根目录src下的permission.js

> 

> 

> 




if (hasToken) { if (to.path === '/login') { next({ path: '/' }) NProgress.done() } else { try { const roles = await store.dispatch('user/getInfo') await store.dispatch('permission/generateRoutes', roles).then(res => { router.addRoutes(res) next({ ...to, replace: true }) }) } catch (error) { await store.dispatch('user/resetToken') Message.error(error || 'Has Error') next(/login?redirect=${to.path}) NProgress.done() } } } else { /* has no token*/ if (whiteList.indexOf(to.path) !== -1) { next() } else { next(/login?redirect=${to.path}) NProgress.done() } }


## 问题一:路由守卫死循环


![在这里插入图片描述](https://p9-xtjj-sign.byteimg.com/tos-cn-i-73owjymdk6/78c68e12cd5942528c236065187ee53d~tplv-73owjymdk6-jj-mark-v1:0:0:0:0:5o6Y6YeR5oqA5pyv56S-5Yy6IEAg55So5oi3NTc5MjMwMTY3MDI=:q75.awebp?rk3s=f64ab15b&x-expires=1772012034&x-signature=hhPjbRcugkzjzEe56QzfOwrwt8g%3D)



> 
> 如图,按上面配置动态路由后,在进入路由守卫时,不断循环,无法正常转发
> 
> 
> 


其实在路由守卫中,只有`next()`是放行,其他的诸如:`next('/logon') 、 next(to) 或者 next({ ...to, replace: true })`都不是放行,而是:**中断当前导航,执行新的导航**


例如现在我有一个守卫,在守卫中我使用`next('/logon')`,肯定有同学认为是会直接跳转到/logon路由:



beforeEach((to, from, next) => { next('/logon') }


其实他是这么执行的:



beforeEach((to, from, next) => { beforeEach(('/logon', from, next) => { beforeEach(('/logon', from, next) => { beforeEach(('/logon', from, next) => { beforeEac... // 一直循环下去...... , 因为我们没有使用 next() 放行 } } } }


`next('/logon')`不是说直接去`/logon`路由,而是中断这一次路由守卫的操作,又进入一次路由守卫,就像嵌套一样,一层路由守卫,然后又是一层路由守卫,此时路由守卫进入到第二层时,`to.path`已经不是`/home`了,这个时候才执行`next()`放行操作。  
 如果守卫中没有正确的放行出口的话,会一直`next({ ...to})`进入死循环 !!!


因此你还需要确保在当`addRoutes()`已经完成时,所执行到的这一次`beforeEach((to, from, next)`中有一个正确的`next()`方向出口。  
 **因此想实现动态添加路由的操作的话,代码应该是这样的:**



const hasToken = getToken()['Authorization'] if (hasToken) { if (to.path === '/login') { next({ path: '/' }) NProgress.done() } else { const hasRoles = store.getters.roles && store.getters.roles.length > 0 if(hasRoles){ next() }else{ try { const roles = await store.dispatch('user/getInfo') await store.dispatch('permission/generateRoutes', roles).then(res => { router.addRoutes(res) next({ ...to, replace: true }) }) } catch (error) { await store.dispatch('user/resetToken') Message.error(error || 'Has Error') next(/login?redirect=${to.path}) NProgress.done() } } } } else { /* has no token*/ if (whiteList.indexOf(to.path) !== -1) { next() } else { next(/login?redirect=${to.path}) NProgress.done() } } })



getInfo({ commit, state }){ return new Promise((resolve, reject) => { const { roles } = JSON.parse(getUser()) commit("SET_ROLES", roles) resolve(roles) }) },


## 问题二:addRouters()不生效




> 

> 执行完addRouters()后,我查看了store里的routes,已经有将动态路由添加进去,但是菜单中还是没有显示

> 

> 

> 





> 

> 查看了/layout/components/Sidebar/index.vue,发现使用的是this. 

>  

>  

>  

>  

>  r 

>  

>  

>  o 

>  

>  

>  u 

>  

>  

>  t 

>  

>  

>  e 

>  

>  

>  r 

>  

>  

>  . 

>  

>  

>  o 

>  

>  

>  p 

>  

>  

>  t 

>  

>  

>  i 

>  

>  

>  o 

>  

>  

>  n 

>  

>  

>  s 

>  

>  

>  . 

>  

>  

>  r 

>  

>  

>  o 

>  

>  

>  u 

>  

>  

>  t 

>  

>  

>  e 

>  

>  

>  s 

>  

>  

>  

>  

>  

>  

>  

>  

>  

>  

>  

>  s 

>  

>  

>  t 

>  

>  

>  o 

>  

>  

>  r 

>  

>  

>  e 

>  

>  

>  

>  

>  

>  

>  

>  

>  

>  

>  

>  

>  

>  

>  

>  

>  

>  

>  

>  

>  

>  

>  

>  

>  

>  

>  

>  

>  

>  

>  

>  

>  a 

>  

>  

>  d 

>  

>  

>  d 

>  

>  

>  R 

>  

>  

>  o 

>  

>  

>  u 

>  

>  

>  t 

>  

>  

>  e 

>  

>  

>  r 

>  

>  

>  s 

>  

>  

>  ( 

>  

>  

>  ) 

>  

>  

>  

>  

>  

>  

>  

>  

>  

>  

>  

>  

>  

>  

>  t 

>  

>  

>  h 

>  

>  

>  i 

>  

>  

>  s 

>  

>  

>  . 

>  

>  

>  

>  router.options.routes而不是store中的,所以我们需要在addRouters()后也修改this. 

>  

>  

>  router.options.routes而不是store中的,所以我们需要在addRouters()后也修改this.router.options.routes的值,或者直接修改下面routes()方法,直接使用store中的routes

> 

> 

> 




routes() { return this.$router.options.routes }


**修改后的permission.js**



const roles = await store.dispatch('user/getInfo') await store.dispatch('permission/generateRoutes', roles).then(res => { router.addRoutes(res) router.options.routes = store.getters.routes next({ ...to, replace: true }) })


## 最终版,所有相关文件


* router/index.js



import Vue from 'vue' import Router from 'vue-router' import Layout from '@/layout'

Vue.use(Router)

export const constantRoutes = [ { path: "", redirect: '/portal', },

{ path: '/login', component: () => import('@/views/login/index'), },

{ path: '/404', component: () => import('@/views/404'), },

{ path: '/portal', component: () => import('@/views/portal/index'), },

{ path: '/', component: Layout, redirect: '/dashboard', children: [{ path: 'dashboard', name: 'Dashboard', component: () => import ('@/views/dashboard/index'), meta: { title: '资源认证平台', affix: true, icon: 'dashboard' } }] },

{ path: '/user', component: Layout, redirect: '/user', children: [{ path: 'user', name: 'User', component: () => import ('@/views/user/index'), meta: { title: '用户管理', affix: true, icon: 'user', } }] } // 404 page must be placed at the end !!! ]

export const asyncRoutes = [ { path: '/application', component: Layout, redirect: '/application', children: [{ path: 'application', name: 'Application', component: () => import ('@/views/application/index'), meta: { title: '应用管理', affix: true, icon: 'table', roles: ['system'] } }] }, { path: '*', redirect: '/404', hidden: true } ]

const createRouter = () => new Router({ scrollBehavior: () => ({ y: 0 }), routes: constantRoutes })

const router = createRouter()

export function resetRouter() { const newRouter = createRouter() router.matcher = newRouter.matcher // reset router }

export default router


* store/modules/permission.js



### 最后

喜欢的话别忘了关注、点赞哦~


> ![前端校招面试题精编解析大全](https://p9-xtjj-sign.byteimg.com/tos-cn-i-73owjymdk6/7cb9fced2cd44ca5a3749f257cf585c7~tplv-73owjymdk6-jj-mark-v1:0:0:0:0:5o6Y6YeR5oqA5pyv56S-5Yy6IEAg55So5oi3NTc5MjMwMTY3MDI=:q75.awebp?rk3s=f64ab15b&x-expires=1772012034&x-signature=CWYW4UqVkTXYj5ARUDRaJkL8xXs%3D)


**开源分享:https://docs.qq.com/doc/DSmRnRGxvUkxTREhO**