作者:MC_炽焰 | 来源:互联网 | 2023-09-14 17:27
Vue-Element-Admin框架在定义路由使用constantRoutes和asyncRoutes两种,在store.modules.permission.js中对于两者的权
Vue-Element-Admin的router总使用constantRoutes和asyncRoutes两种定义路由,在store.modules.permission.js中对于两者的权限有默认的判断方式,然后再根目录下的permission.js中进行导航守卫逻辑;
constantRoutes: 不需要动态判断权限的路由,如登录页、404、等通用页面;
asyncRoutes: 需动态判断权限并通过 addRoutes 动态添加的页面;
一、vue-element-admin中动态路由实现
router文件夹下的index.js是项目总路由入口,index.js对模块注册后默认将constantRoutes无权限的路由增加进去,在store/modules/permission.js中维护静态+动态路由权限,并在根目录下的permission.js实现导航守卫逻辑,在/router/index.js中:
// router/index.js 部分代码
// 模块化,首先引入在modules目录下需要用的router
import componentsRouter from './modules/components'
import chartsRouter from './modules/charts'
import tableRouter from './modules/table'
import nestedRouter from './modules/nested'
import yzgTest from './modules/yzg'
//constantRoutes 是静态路由,不需要动态权限判断
export const cOnstantRoutes= []
//constantRoutes 是动态路由,需要动态权限判断
export const asyncRoutes = []
//
const createRouter = () => new Router({
// mode: 'history', // require service support
scrollBehavior: () => ({ y: 0 }),
routes: constantRoutes
})
const router = createRouter()
// Detail see: https://github.com/vuejs/vue-router/issues/1234#issuecomment-357941465
export function resetRouter() {
const newRouter = createRouter()
router.matcher = newRouter.matcher // reset router
}
export default router
在store/modules/permission.js中维持了一个全局state数组,维持当前登录用户所拥有的菜单权限,并且在路由守卫时候完成动态路由的切换,store/modules/permission.js中动态路由state状态的维护逻辑如下:
// 首先,从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('admin')) {
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
}
最后再看根目录下permission.js中路由守卫的逻辑,
// vue-element-admin中permission.js中导航守卫逻辑
import router from './router'
import store from './store'
import { Message } from 'element-ui'
import NProgress from 'nprogress' // progress bar
import 'nprogress/nprogress.css' // progress bar style
import { getToken } from '@/utils/auth' // get token from COOKIE
import getPageTitle from '@/utils/get-page-title'
// 进度条
NProgress.configure({ showSpinner: false }) // NProgress Configuration
// 白名单
const whiteList = ['/login', '/auth-redirect'] // no redirect whitelist
// 路由之前,逻辑判定
router.beforeEach(async(to, from, next) => {
// 开始进度条
NProgress.start()
// set page title
document.title = getPageTitle(to.meta.title)
// determine whether the user has logged in
const hasToken = getToken()
// 有token,如果想去login则调到首页,如果是其他页面先判定是否有角色,有的话就跳过去,没有的话发请求得到永不信息,再调用函数维护store路由列表,报错要么没权限,要么是请求超时,就要返回error,清除token,返回登录页
// 没有token,且不是白名单内的,返回登录页
if (hasToken) {
if (to.path === '/login') {
// if is logged in, redirect to the home page
next({ path: '/' })
NProgress.done() // hack: https://github.com/PanJiaChen/vue-element-admin/pull/2939
} else {
// determine whether the user has obtained his permission roles through getInfo
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) {
// remove token and go to login page to re-login
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 {
// other pages that do not have permission to access are redirected to the login page.
next(`/login?redirect=${to.path}`)
NProgress.done()
}
}
})
// 进度条完毕
router.afterEach(() => {
// finish progress bar
NProgress.done()
})
asyncRoutes中刻意使用meta.roles定义角色,因此在后端请求返回用户的角色后,在permission.js中能够挂的上判断逻辑;
二、vue中路由
vue-router是Vue.js官方的路由插件,和vue.js深度集成,用于设定访问路径,并将路径和组件映射起来。传统的页面应用,是用一些超链接来实现页面切换和跳转的。在vue-router单页面应用中,则是路径之间的切换,也就是组件的切换。路由模块的本质 就是建立起url和页面之间的映射关系。vue-router在实现单页面前端路由提供Hash模式和History两种模式,
router.push(location)=window.history.pushState
想要导航到不同的 URL,则使用 router.push 方法。这个方法会向 history 栈添加一个新的记录,所以,当用户点击浏览器后退按钮时,则回到之前的 URL;