动态路由主要采用的是router.addRoutes添加动态路由
思路分析
- 前后端数据需要进行对比,动态路由需要给父级路由设置默认重定向
- 在utils包中定义两个方法
- 主要采用递归的方式
- 采用路由导航守卫,对路由跳转进行监控,判断其是否登录,是否有权限,并添加动态路由等
- 在store中处理动态路由
代码实现
- 导航守卫
import router from './index'
import store from '../store/index'
router.beforeEach((to, from, next) => {
if (!store.state.userToken) {
// 未登录
if (!to.name || to.matched.some(record => record.meta.requireAuth)) {
// 页面需要登录
// matched存放了路径的所有路由,包括父路由和子路由
// 父路由或子路由中的任意一个路由‘requireAuth’为真,都要跳转到登录页
// 也就是在设置时,只需要将父路由的requireAuth=true,就可以控制所有子路由了
next({ path: '/login' })
} else {
next()
}
} else {
// 已登录 判断权限
if (to.path === '/login') {
// 已登录不能再去登录页面
next('/')
} else if (store.state.permissions.permissionRoutes.length === 0) {
// 动态路由还未加载
// 加载动态路由
store.dispatch('setRoutes', store.state.userToken).then(routes => {
// 添加动态路由
router.addRoutes(routes)
// 使用以下方法,可以中断当前导航,再次进入导航守卫,这样可以确保addRoutes已完成
next({ ...to, replace: true })
})
} else {
if (to.name) {
next()
} else {
// 路由不存在就去404页面
next('/404')
}
}
}
})
- store中处理动态路由
import { getPermissions } from '../../api/permission.js'
import { dynamicDefaultRoutes } from '@/router/index.js'
import { diffRoutes, defaultRoute } from '@/utils/deal-route.js'
import dynamicRoutes from '../../router/dynamic-routes'
export default {
state: {
permissionRoutes: [] // 存储动态路由
},
mutations: {
SET_ROUTES(state, routes) {
state.permissionRoutes = routes
},
CLEAR_ROUTES(state) {
state.permissionRoutes = null
}
},
actions: {
async setRoutes({ commit }, token) {
// 获取后台路由权限
let res = await getPermissions({ user: token })
// 对比后台权限与前端所有动态路由,得到有权限的路由
let routes = diffRoutes(dynamicRoutes, res.data)
// 处理动态路由的重定向
defaultRoute(routes)
// 获取默认路由中的layout根路由,并将动态路由设置为其子路由
let container = dynamicDefaultRoutes.find(r => r.path === '')
container.children.push(...routes)
commit('SET_ROUTES', dynamicDefaultRoutes)
// 返回动态路由,在路由导航中添加动态路由
return dynamicDefaultRoutes
}
}
}
- utils对比动态路由和设置默认路由方法
// 对比前后端路由,获取有权限的动态路由
export function diffRoutes(allroutes = [], userRoutes = []) {
const realRoutes = []
allroutes.forEach(r => {
userRoutes.forEach(item => {
if (r.meta.name === item.name) {
if (item.children && item.children.length > 0) {
// 递归处理子路由
r.children = diffRoutes(r.children, item.children)
} else {
r.children = []
}
realRoutes.push(r)
}
})
})
return realRoutes
}
// 为路由设置重定向,默认是当前路由的第一个子路由
export function defaultRoute(allroutes) {
allroutes.forEach(r => {
if (r.children && r.children.length > 0) {
r.redirect = r.path + '/' + r.children[0].path
// 递归处理子路由
defaultRoute(r.children)
}
})
}
注意: vue-router新版本下,router.addRoutes已被废弃;使用 route.addRoute() 代替
for (let x of routes) { router.addRoute(x) }