Vue配置动态路由

297 阅读2分钟

前言

最近刚好写了自己搭了一个后台,刚好做了这个功能,所以分享出来,其方案是使用vuerouter的addRoute、分享代码给大家,具体情况需要根据自己的业务调整

官方定义

image.png 网上大多数都是使用的addRoutes,但是官方已经废弃了这个API

image.png

实现

路由菜单都是从后台获取的,我们只需要,要求后端返回的菜单里面,包含对应前端的文件路径,用来加载页面组件

定义一个路由文件, 在完成所有的逻辑后,我们只需要维护权限加载路由和默认导入路由即可, 具体看我下面的代码和注释

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()
}