Vue动态路由的实现

3,159 阅读3分钟

注释: 代码实现过程借鉴了海军大哥的代码

vue动态路由分俩种情况

  • 第一种是前端实现, 前端通过不同角色对路由进行筛选,之后在侧边栏进行展示
  • 第二种是后端实现,登录之后通过后端返回的menuList,在前端进行解析

今天主要讲解第二种情况

登录部分代码 (登录接口以及返回路由结果均有mock实现)
file: @/views/login

onSubmit (a) {
      login(a).then(res => {
        const { name, photo, token } = res.data // 结构
        sessionStorage.setItem('token', token)
        this.$store.commit('SET_NAME', name)
        this.$store.commit('SET_PHOTO', photo)
        this.$store.commit('SET_TOKEN', token)
        this.$store.dispatch('setMenuList') // 调用方法对路由解析
        this.$router.push('/home')
      })
}
通过调用vuex中setMenuList的方法对后端返回的路由进行解析
file:@/store/index.js

import Vue from 'vue'
import Vuex from 'vuex'
import router, { constantRoutes } from '../router/index'
// getMenu 解析后台路由
import { getMenu } from '../utils/getMenu'
Vue.use(Vuex)

export default new Vuex.Store({
  state: {
    meunList: []
  },
  mutations: {
    SET_ROUTER_MENULIST (state, list) {
      // 静态路由 +  动态路由  合并  完整路由
      const array = constantRoutes.concat(list)
      state.meunList = array
      router.options.routes = array
      /**
         * 新版router方法 废弃了router.addRoutes方法
         */
      array.forEach(itemRouter => {
        router.addRoute(itemRouter)
      })
      const errorPage = {
        path: '*',
        redirect: '/404',
        hidden: true
      }
      /** 刷新界面进入404解决:
      *原因在于,我们在addRoutes加动态路由前,就配置了通配符404路由;
        * 最后改成把动态添加过路由后吗,再最后push一下404通配符
        * 这之前有一个bug 如果不在addroute之后push一次通配符 在动态路由界面刷新就会进入404界面
      */
      array.push(errorPage)
      router.addRoute(array[array.length - 1])
    }
  },
  actions: {
    setMenuList ({ commit, state }) {
      // 接收返回来的 路由数组
      return new Promise((resolve, reject) => {
        getMenu().then(res => { // getMenu接下来会写
          commit('SET_ROUTER_MENULIST', res)
          // resolve(res)
        })
      })
    }
  }
})
getMenu主要用来解析后台返回的结果
file: @/utils/getMenu.js


/* eslint-disable no-unused-vars */
import Layout from '@/layout'
import { login } from '@/api/login' // 登录接口

/**
 * @description: 解析后端返回来的菜单树
 * @param {*} data 后端返回来的路由树
 * @param {*} arr 菜单
 * @return {*}
 */
function tree (data, arr) {
  // console.log(data);
  data.forEach((datas, index) => {
    arr.push({
      path: datas.path,
      name: datas.name,
      hidden: !!datas.hidden,
      //   types: datas.types,
      // hidden: datas.hidden == 'true' ? true : false,
      // 当时这块踩坑了
      component: datas.component === 'Layout' ? Layout : resolve => require([`@/views/${datas.component}`], resolve),
      meta: {
        title: datas.meta.title || '',
        icon: datas.meta.icon || '',
        path: datas.meta.path || ''
        // // 用来存放按钮权限
        // button: datas.meta.button
      },
      redirect: datas.redirect || '',
      // id: datas.id,
      // 子路由
      children: []
    })
    // console.log(arr);

    if (datas.children) {
      const childArr = tree(datas.children, [])
      arr[index].children = childArr
    }
  })
  return arr
}

/**
 * @description: 获取当前登录用户的菜单
 * @param {*}
 * @return {*}
 */
export function getMenu () {
  return new Promise(function (resolve, reject) {
    var token = sessionStorage.getItem('token')
    var userData
    if (token === 'admin') {
      userData = {
        account: 'wjc',
        password: '000919'
      }
    } else {
      userData = {
        account: 'test',
        password: 'test'
      }
    }
    login(userData).then(res => {
      const datas = res.data.rightList
      // 调用 tree 来解析后端返回来的树
      resolve(tree(datas, []))
    })
  })
}

login 为登录接口 账号密码写死了 通过不同token对应不同账号获得不同的路由
login接口代码(mock)
file: @/mock/login
export default {
  /** 用户登录
     *@login
     */
login: (config) => {
    const user = getQueryParameters(config)
    if (user.account === 'wjc' && user.password === '000919') {
      return {
        name: '管理员',
        token: 'admin',
        photo: 'https://img1.baidu.com/it/u=3364871237,1751868068&fm=15&fmt=auto',
        rightList: [
          {
            path: '/home',
            name: 'home',
            redirect: '/home',
            component: 'Layout',
            meta: {
              icon: 'el-icon-s-home',
              title: 'Dashboard',
              path: '/home'
            },
            children: [
              {
                path: '/home',
                name: '',
                component: 'home',
                meta: {
                  icon: 'el-icon-s-home',
                  title: 'Dashboard',
                  path: '/home'
                }
              }
            ]
          },
          {
            path: '/dataCenter',
            name: '',
            component: 'Layout',
            redirect: '/table',
            meta: {
              icon: 'el-icon-s-data',
              title: '基本组件'
            },
            children: [
              {
                path: '/table',
                name: '',
                component: 'dataCenter/table',
                meta: {
                  title: '动态表格',
                  path: '/table'
                },
                children: []
              },
              {
                path: '/form',
                name: '',
                component: 'dataCenter/form',
                meta: {
                  title: '动态表单',
                  path: '/form'
                },
                children: []
              }
            ]
          },
          {
            path: '/mark',
            name: '',
            component: 'Layout',
            redirect: '/editmark',
            meta: {
              icon: 'el-icon-menu',
              title: 'Markdown'
            },
            children: [
              {
                path: '/editmark',
                name: '',
                component: 'mark/editmark',
                meta: {
                  title: '编辑Markdown',
                  path: '/editmark'
                },
                children: []
              },
              {
                path: '/showmark',
                name: '',
                component: 'mark/showmark',
                meta: {
                  title: '展示Markdown',
                  path: '/showmark'
                },
                children: []
              }
            ]
          },
          {
            path: '/rolebutton',
            name: 'rolebutton',
            component: 'Layout',
            redirect: '/rolebutton',
            meta: {
              icon: 'el-icon-mouse',
              title: '指令按钮',
              path: '/rolebutton'
            },
            children: [
              {
                path: '/rolebutton',
                name: '',
                component: 'rolebutton',
                meta: {
                  icon: 'el-icon-mouse',
                  title: '指令按钮',
                  path: '/rolebutton'
                }
              }
            ]
          },
          {
            path: '/drawingbed',
            name: 'drawingbed',
            component: 'Layout',
            redirect: '/drawingbed',
            meta: {
              icon: 'el-icon-picture',
              title: '图床组件',
              path: '/drawingbed'
            },
            children: [
              {
                path: '/drawingbed',
                name: '',
                component: 'drawingbed',
                meta: {
                  icon: 'el-icon-picture',
                  title: '图床组件',
                  path: '/drawingbed'
                }
              }
            ]
          },
          {
            path: '/power',
            name: 'power',
            component: 'Layout',
            redirect: '/power',
            meta: {
              icon: 'el-icon-s-tools',
              title: '权限页面',
              path: '/power'
            },
            children: [
              {
                path: '/power',
                name: '',
                component: 'power',
                meta: {
                  icon: 'el-icon-s-tools',
                  title: '权限页面',
                  path: '/power'
                }
              }
            ]
          }
        ]
      }
    } else {
      return {
        name: '测试员',
        role: 0,
        token: 'test',
        photo: 'https://img0.baidu.com/it/u=1244881529,3297907499&fm=26&fmt=auto',
        rightList: [
          {
            path: '/home',
            name: 'home',
            redirect: '/home',
            component: 'Layout',
            meta: {
              icon: 'el-icon-s-home',
              title: 'Dashboard',
              path: '/home'
            },
            children: [
              {
                path: '/home',
                name: '',
                component: 'home',
                meta: {
                  icon: 'el-icon-s-home',
                  title: 'Dashboard',
                  path: '/home'
                }
              }
            ]
          },
          {
            path: '/dataCenter',
            name: '',
            component: 'Layout',
            redirect: '/table',
            meta: {
              icon: 'el-icon-s-data',
              title: '基本组件'
            },
            children: [
              {
                path: '/table',
                name: '',
                component: 'dataCenter/table',
                meta: {
                  title: '动态表格',
                  path: '/table'
                },
                children: []
              },
              {
                path: '/form',
                name: '',
                component: 'dataCenter/form',
                meta: {
                  title: '动态表单',
                  path: '/form'
                },
                children: []
              }
            ]
          },
          {
            path: '/mark',
            name: '',
            component: 'Layout',
            redirect: '/editmark',
            meta: {
              icon: 'el-icon-menu',
              title: 'Markdown'
            },
            children: [
              {
                path: '/editmark',
                name: '',
                component: 'mark/editmark',
                meta: {
                  title: '编辑Markdown',
                  path: '/editmark'
                },
                children: []
              },
              {
                path: '/showmark',
                name: '',
                component: 'mark/showmark',
                meta: {
                  title: '展示Markdown',
                  path: '/showmark'
                },
                children: []
              }
            ]
          },
          {
            path: '/rolebutton',
            name: 'rolebutton',
            component: 'Layout',
            redirect: '/rolebutton',
            meta: {
              icon: 'el-icon-mouse',
              title: '指令按钮',
              path: '/rolebutton'
            },
            children: [
              {
                path: '/rolebutton',
                name: '',
                component: 'rolebutton',
                meta: {
                  icon: 'el-icon-mouse',
                  title: '指令按钮',
                  path: '/rolebutton'
                }
              }
            ]
          },
          {
            path: '/drawingbed',
            name: 'drawingbed',
            component: 'Layout',
            redirect: '/drawingbed',
            meta: {
              icon: 'el-icon-picture',
              title: '图床组件',
              path: '/drawingbed'
            },
            children: [
              {
                path: '/drawingbed',
                name: '',
                component: 'drawingbed',
                meta: {
                  icon: 'el-icon-picture',
                  title: '图床组件',
                  path: '/drawingbed'
                }
              }
            ]
          }
        ]
      }
    }
   }
 }
router里面只需要放一些静态路由就行 注意!!! 不能把通配符路由放进静态路由 刷新页面会进入404 通配符路由一定要最后push以下,解释在上面
file: @/router/index.js

import Vue from 'vue'
import VueRouter from 'vue-router'
Vue.use(VueRouter)
export const constantRoutes = [
  {
    path: '/',
    redirect: '/login',
    hidden: true
  },
  {
    path: '/login',
    name: 'login',
    component: () => import('@/views/login'),
    hidden: true
  },
  {
    path: '/404',
    name: '404',
    component: () => import('@/views/404'),
    hidden: true
  }
]
const createRouter = () => new VueRouter({
  mode: 'history',
  // 解决vue框架页面跳转有白色不可追踪色块的bug
  scrollBehavior (to, from, savedPosition) {
    return { x: 0, y: 0 }
  },
  routes: constantRoutes
})
const router = createRouter()


export default router


整体思路就是前端登录成功 调用vuex的setMenulist方法 之后在里面掉getMenu的方法的结果 接下来就是调用mutations中的方法把静态路由和后端返回的路由结合起来 之后addRoute