addroutes刷新_vue-router之解决addRoutes使用遇到的坑

最近项目中使用了vue-router的addRoutes这个api,遇到了一个小坑,记录总结一下。

场景复现:

做前端开发的同学,大多都遇到过这种需求:页面菜单根据用户权限动态生成,一个常见的解决方案是:

前端初始化的时候,只挂载不需要权限路由,如登陆,注册等页面路由,然后等用户登录之后,后端返回当前用户的权限表,前端根据这个权限表遍历前端路由表,动态生成用户权限路由,然后使用vue-router提供的addRoutes,将权限路由表动态添加到路由实例中,整个过程大致如下:

// router.js 文件

// 需要用户权限的路由表

const appRoutes = [

{

path: '/dashboard',

name: 'dashboard',

component: () => import('...'),

children: [

RouteConfig1,

RouteConfig2,

...

]

},

RouteConfig,

...

];

// 不需要用户权限的路由表

const constantRoutes = [

{

path: '/login',

name: 'login',

component: Login

},

{

path: '/register',

name: 'register',

component: Register

},

...

]

// 初始化路由的时候,只挂载不需要用户权限的路由表

const router = new VueRouter({

mode: 'history',

base: process.env.BASE_URL,

constantRoutes

});

/**

*

* 假如后端返回的数据格式如下:

*

* {

* status: 200,

* message: 'successful',

* data: {

* user: {...},

* token: '...',

* permisssion: [...]

* }

* }

*

* login.vue

*/

axios.post('/user/login',{username,password})

.then(res => {

if (res.status === 200) {

// 如果登录成功,则需要遍历生成用户权限路由

// filterRoutes根据permission和router.js中定义的appRoutes生成动态路由表

const routes = filterRoutes(permission);

// 然后使用addRoutes将routes挂载到router中

router.addRoutes(routes);

} else {

...

}

})

.catch(error => { ... })

写到这里,貌似动态生成路由的功能就好了,一切都perfect了,但问题紧接着就来了,当用户登录之后,我们点击页面上的退出按钮退出当前登录,然后重新登录,会发现浏览器console面板紧接着就报如下错误:

纳尼(⊙o⊙)?这是怎么回事呢,第二次登录也正常登录了,功能上似乎没有什么问题,但这个警告从哪里来的呢?对于一个重度强迫症患者来说,任何警告和报错都是不允许出现的,哪怕功能上没什么问题。

捋一捋

这段警告的意思是说,以上的这几个路由命名重复,存在多个name相同的路由。那么为什么会有多个路由名称相同的路由呢?

让我们从头捋一下这个错误是怎么来的。首先第一次打开网站登录的时候是没有问题的,只有当我们退出登录,重新登录的时候,这段警告就来了。并且如果我们在重复登录之前刷新一下浏览器然后再登录,这种警告就不会出现了,很神奇是不是?

分析一下上面的情景:首先这个警告只会在用户重新登录的时候出现,登录的时候我们做的唯一跟路由相关的事情就是动态添加路由,所以问题肯定出在 router.addRoutes(routes)这里,其次这里又分了两种情况:有刷新和无刷新。在无刷新的情况下会报这个警告,有刷新就不会报这个警告。那么有刷新和无刷新有什么区别呢?

我们很容易就想到,当页面刷新的时候,Vue实例会重新初始化,Vue实例初始化的过程中,挂载在它上面的Vue-Router,Store等内容也会重新初始化。而在不刷新的情况下,就不会重新初始化。

再想想,我们第一次登录之后,通过addRoutes添加了权限路由routes到router上,假设我们这个权限routes中包括了dashboard,user,role三个路由,那么当我们退出登录,然后重新登录的时候,由于同一个用户登录,后端返回的权限列表是一样的,生成的动态路由routes也是一样的(即里面同样包含了dashboard,user,role三个路由),那么此时再次添加这三个路由就导致router中挂载的routes重复。而在刷新的情况下,由于router重新初始化,只包含了初始化我们添加的不需要权限的路由,此时再次登录,重新添加就不存在路由重复的问题了。

通过以上的分析,我们搞清了问题的来源,那么如何解决呢,很遗憾,vue-router并没有删除路由的api。根据以上的分析,我们很容易想到,通过强制刷新页面的方式来重置router:即当用户退出登录的时候,通过js强制刷新一下页面。就可以解决问题。这种方式虽然可以解决问题,但显得不是很优雅,而且刷新页面导致资源重新加载和页面闪烁,体验也不是特别好。因此有没有在不刷新的情况下解决问题的办法呢?

经过一番搜索,终于找到了一种方法,即重置当前router的match属性:

router.js

// 定义一个函数来创建router

export const createRouter = routes => new VueRouter({

mode: 'history',

base: process.env.BASE_URL,

routes

});

// 在使用addRoutes的地方

// 重置当前router的match = 初始router.match

router.match = createRouter(constantRoutes).match;

router.addRoutes(routes);

这样就可以完美解决问题了。

总结:

整个解决的过程还是比较痛苦的,因为实际中我的代码是比较复杂的,并不像上面简化后那么简单。

整个addRoutes是在store.dispatch中完成,并且中间还夹杂着生成动态路由,根据动态路由再生成用户菜单等一系列功能,干扰比较大,并且这个是源码报警,不好定位,只能通过console和浏览器调试,一步步缩小报错范围,最终找到问题原因。

然后再通过google,以及搜索vue-router仓库的issue一步步找到解决方法。

所以想说,如果大家开发中遇到一些第三方依赖的问题,可以去搜索官方仓库的issue,很好用的,很多问题其实issue中都有答案。我是屡试不爽。

最后,一定要用google,百度,浪费我好长时间,啥都没找到~

时间: 2020-07-17

  • 5
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
动态路由是指路由表在运行时动态生成,而不是在编译时生成。在 Vue.js 中,可以使用 Vue Router 实现动态路由。下面是一个简单的步骤,可以帮助你搭建一个基于 Vue-admin-template 的动态路由。 1. 安装 Vue Router ```bash npm install vue-router --save ``` 2. 在 src/router/index.js 中引入 Vue Router,并创建一个路由实例 ```javascript import Vue from 'vue'; import Router from 'vue-router'; import Layout from '@/layout'; Vue.use(Router); export const constantRoutes = [ // 静态路由 { path: '/login', component: () => import('@/views/login/index'), hidden: true }, { path: '/404', component: () => import('@/views/404'), hidden: true }, { path: '/', component: Layout, redirect: '/dashboard', children: [{ path: 'dashboard', name: 'Dashboard', component: () => import('@/views/dashboard/index'), meta: { title: 'Dashboard', icon: 'dashboard' } }] } ]; const createRouter = () => new Router({ mode: 'history', // 启用 history 模式 scrollBehavior: () => ({ y: 0 }), routes: constantRoutes }); const router = createRouter(); export default router; ``` 3. 在 src/store/modules/permission.js 中创建一个动态路由生成函数 ```javascript import { constantRoutes } from '@/router'; /** * 递归过滤异步路由表,返回符合用户角色权限的路由表 * @param routes asyncRoutes * @param roles */ 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; } /** * 判断用户角色是否有权限访问该路由 * @param roles * @param route */ function hasPermission(roles, route) { if (route.meta && route.meta.roles) { return roles.some(role => route.meta.roles.includes(role)); } else { return true; } } const state = { routes: [], addRoutes: [] }; const mutations = { SET_ROUTES: (state, routes) => { state.addRoutes = routes; state.routes = constantRoutes.concat(routes); } }; const actions = { generateRoutes({ commit }, roles) { return new Promise(resolve => { let accessedRoutes; if (roles.includes('admin')) { accessedRoutes = asyncRoutes || []; } else { accessedRoutes = filterAsyncRoutes(asyncRoutes, roles); } commit('SET_ROUTES', accessedRoutes); resolve(accessedRoutes); }); } }; export default { namespaced: true, state, mutations, actions }; ``` 4. 在 src/router/index.js 中引入动态路由生成函数,并在路由实例中使用 ```javascript import Vue from 'vue'; import Router from 'vue-router'; import Layout from '@/layout'; import store from '@/store'; Vue.use(Router); export const constantRoutes = [ // 静态路由 { path: '/login', component: () => import('@/views/login/index'), hidden: true }, { path: '/404', component: () => import('@/views/404'), hidden: true }, { path: '/', component: Layout, redirect: '/dashboard', children: [{ path: 'dashboard', name: 'Dashboard', component: () => import('@/views/dashboard/index'), meta: { title: 'Dashboard', icon: 'dashboard' } }] } ]; const createRouter = () => new Router({ mode: 'history', // 启用 history 模式 scrollBehavior: () => ({ y: 0 }), routes: constantRoutes }); const router = createRouter(); // 刷新页面时重新生成动态路由 router.beforeEach(async(to, from, next) => { const hasRoles = store.getters.roles && store.getters.roles.length > 0; if (hasRoles) { next(); } else { try { // 获取用户角色信息 const { roles } = await store.dispatch('user/getInfo'); // 生成动态路由 const accessedRoutes = await store.dispatch('permission/generateRoutes', roles); // 添加动态路由 router.addRoutes(accessedRoutes); next({ ...to, replace: true }); } catch (error) { console.log(error); } } }); export function resetRouter() { const newRouter = createRouter(); router.matcher = newRouter.matcher; // reset router } export default router; ``` 以上是一个基于 Vue-admin-template 的动态路由搭建步骤,你可以根据自己的需求进行修改和扩展。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值