前言
想必大家都知道,在 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()
将其添加到路由中,这样就完成了整个动态路由生成的流程。
最后补上脑图: