前言
最近刚好写了自己搭了一个后台,刚好做了这个功能,所以分享出来,其方案是使用vuerouter的addRoute、分享代码给大家,具体情况需要根据自己的业务调整
官方定义
网上大多数都是使用的addRoutes,但是官方已经废弃了这个API
实现
路由菜单都是从后台获取的,我们只需要,要求后端返回的菜单里面,包含对应前端的文件路径,用来加载页面组件
定义一个路由文件, 在完成所有的逻辑后,我们只需要维护权限加载路由和默认导入路由即可, 具体看我下面的代码和注释
import Vue from 'vue'
import VueRouter from 'vue-router'
import login from './modules/login'
import common from './modules/common'
import project from './modules/project'
import system from './modules/system'
import error from '@/router/modules/error'
Vue.use(VueRouter)
// 权限加载的路由
export const defaultRoutes = [
...common,
...project,
...system
]
// 默认导入的路由、用于前端开发的时候或者不需要权限的
export const routes = [
...login,
...error
]
const createRouter = () => new VueRouter({
base: process.env.BASE_URL,
routes
})
const router = createRouter()
// 初始化路由、用于退出登录或者后端返回权限不足的情况
export function resetRouter () {
const newRouter = createRouter()
router.matcher = newRouter.matcher
}
export default router
在main.js同级下、定义一个menu.js
import { defaultRoutes } from '@/router'
// 格式化路由结构、平铺
const formatRoutes = function (array) {
const list = []
if (Array.isArray(array) && array.length) {
array.forEach(item => {
list.push(item)
if (Array.isArray(item.children) && item.children.length) {
list.push(...formatRoutes(item.children))
}
})
}
return list
}
// 添加路由到实例上面
export const addRoutes = function (array) {
if (Array.isArray(array) && array.filter(f => f).length) {
array.forEach(menu => {
const { name, children } = menu
const isLen = Array.isArray(children) && children.length
isLen ? this.$router.addRoute(name, menu) : this.$router.addRoute(menu)
isLen && addRoutes.call(this, children)
})
}
}
// 添加菜单, 过滤、这里对应了一些后端的字段
const addMenu = function (array, defaultArray) {
if (Array.isArray(array) && array.length) {
return array.map(item => {
const { url, id, icon, name, children, path, meta } = item
const itemPath = path || url || String(id)
const defaultObj = {
path: itemPath,
children: [],
meta: meta || { icon, title: name, hidden: false }
}
const routeItem = defaultArray.find(f => f.path === itemPath) || defaultObj
const { children: routeItemChildren } = routeItem
return {
...routeItem,
children: Array.isArray(children) && children.length ? addMenu(children, routeItemChildren) : []
}
})
}
return []
}
// 过滤路由
const filterRoutes = function (array, defaultArray, parentPath = '') {
return defaultArray
.map(item => {
if (array.find(f => parentPath ? `${parentPath}/` : '' + (f.path || f.url) === item.path)) {
const isLen = Array.isArray(item.children) && item.children.length
return {
...item,
children: isLen ? filterRoutes(array, item.children, item.path) : []
}
}
return null
})
.filter(f => f)
}
/**
* // 生成菜单 需要绑定this 后台设置路由的时候,需要对应前端的文件夹
* @param array
*/
export const generateMenu = function (array) {
const permissionsRoutes = filterRoutes(formatRoutes(array), defaultRoutes)
addRoutes.call(this, permissionsRoutes)
const menu = addMenu(array, formatRoutes(permissionsRoutes))
// 缓存路由、防止刷新后消失, 这里是存到vuex和本地local,做了简陋的持久化缓存、
// 进来的时候,在APP.vue调用generateMenu这个方案即可
this.SET_MENU(menu)
// 添加错误页 需要在最后添加,不然一进来页面就会404
this.$router.addRoute({
path: '/:path(.*)*',
redirect: '/404',
name: '404Page',
meta: {
hidden: true
}
})
输出所有路由
console.log(this.$router.getRoutes())
}
请求方面
我们在vuex定于好mutations,当axios请求401,也就是后端返回token过期或者权限不足的时候,直接调用即可,在最后面记得调用resetRouter这个方法,重置路由
CLEAR_AUTH (state) {
state.token = ''
state.userInfo = ''
state.menuList = ''
REMOVE_STORAGE(TOKEN)
REMOVE_STORAGE(USERINFO)
REMOVE_STORAGE(MENU)
// 重置路由
resetRouter()
}