前言
想必大家都知道,在 vue 中实现权限管理的核心方法是 vue-router 中的路由守卫,那么这里我们全局搜索一下 vue-router 的钩子看看有什么线索:
显然,在 vue-element-admin 中,实现权限管理的地方是 src/permission.js 中。
分析
可以看到,这里的宏观架构如下所示:
router.beforeEach(async (to, from, next) => { /* do something */ }) router.afterEach(() => { /* do something */ }) 复制代码
其中 afterEach 中做的事很简单:NProgress.done(),作用是结束顶部的 router 跳转进度条。
接下来我们看看 beforeEach 中具体的逻辑是怎么样的。
router.beforeEach(async (to, from, next) => { NProgress.start() document.title = getPageTitle(to.meta.title) const hasToken = getToken() 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') const accessRoutes = await store.dispatch( 'permission/generateRoutes', roles ) router.addRoutes(accessRoutes) next({ ...to, replace: true }) } catch (error) { await store.dispatch('user/resetToken') Message.error(error || 'Has Error') next(`/login?redirect=${to.path}`) NProgress.done() } } } } else { if (whiteList.indexOf(to.path) !== -1) { next() } else { next(`/login?redirect=${to.path}`) NProgress.done() } } }) 复制代码
乍一眼似乎有很多的 if-else 嵌套,实际上这里的逻辑已经写的相当清晰了,仔细看一下就能分析出其中的逻辑:
这里有一些小细节值得一提:
- 在重定向 next 之后要手动的结束 NProgress,具体原因可以看看这个 issue
- next({ ...to, replace: true })中 ,设置 replace: true 的作用是清除导航记录
接下来我们顺着上面的思路,深入去看看这里是如何动态生成路由的:
generateRoutes
generateRoutes({ commit }, roles) { return new Promise(resolve => { let accessedRoutes if (roles.includes('admin')) { accessedRoutes = asyncRoutes || [] } else { accessedRouteszuizho = filterAsyncRoutes(asyncRoutes, roles) } commit('SET_ROUTES', accessedRoutes) resolve(accessedRoutes) }) }de 复制代码
显然,accessedRoutes 就是最终生成的路由表。这里的逻辑也很简单,根据权限确定是否需要过滤 asyncRoutes 中的部分路由即可,而 asyncRoutes,我们可以看到,它来自 router 中:
export const asyncRoutes = [ /* ... */ ] 复制代码
是一个定义好的路由表,再结合上面的逻辑,不难看出,当权限为 admin 的时候,能访问所有路由,否则讲有部分路由根据对应权限被过滤掉。
于是我们来看看过滤逻辑:
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 } 复制代码
显然,这里通过递归遍历整个路由表,如果有权限,就将其存入路由表中,否则直接过滤掉。
最后再回到 src/permission.js 中,再获取到动态生成的路由表后,通过 router.addRoutes() 将其添加到路由中,这样就完成了整个动态路由生成的流程。