Vue-Router 动态路由(动态菜单)的开发规范

12,416 阅读1分钟

小知识,大挑战!本文正在参与“程序员必备小知识”创作活动。

本文同时参与 「掘力星计划」  ,赢取创作大礼包,挑战创作激励金

动态路由(动态菜单)的开发规范

场景:因各个系统的菜单权限管理不同,导致后期维护的难道较高。因菜单权限的管理流程是一致的,所以我们需要用好框架本身自带功能的优势来开发

前提:使用vue框架的动态路由(router)功能来实现

路由(router)的数据结构

import Vue from 'vue'
import Router from 'vue-router'

Vue.use(Router)
const routes = [
 {
   path: '/permission',
   component: Layout,
   redirect: '/permission/page',
   name: 'Permission',
   meta: {
     title: '权限管理',
     icon: 'lock',
     roles: ['super_admin', 'editor']
   },
   children: [
     {
       path: 'role',
       component: () => import('@/views/permission/role'),
       name: 'RolePermission',
       meta: {
         title: '角色信息',
         roles: ['super_admin']
       }
     }
   ]
 },
 {
   path: '/icon',
   component: Layout,
   children: [
     {
       path: 'index',
       component: () => import('@/views/icons/index'),
       name: 'Icons',
       meta: { title: 'Icons', icon: 'icon', noCache: true }
     }
   ]
 }
]

const router = new Router({
  routes: routes
})

export default router

上面是前端项目最基本的路由结构,而唯一缺点就是,路由完全写死的,若想实现动态菜单时则会出现漏洞问题,因此我们需要用后台返回动态菜单与动态路由(router)的addRoutes功能,实现动态注册路由

以下例子:

1、调用后台接口,获取用户权限菜单列表

let Layout =  (resolve) => require([`@/views/Layout`], resolve) // 菜单栏组件
// 从服务器获取路由表
getAsyncRoutes().then(routes => {
    // 格式化路由表
    const accessedRoutes = formatRoutes(routes, Layout) 
    // 将路由保存到Vuex或者本地缓存中
    localStorage.setItem('accessedRoutes', JSON.stringify(accessedRoutes))
})

2、处理后台返回的路由表

// 根据后台返回的路径,生成页面的组件模版
function loadView(component) {
  return (resolve) => require([`@/views/${component}`], resolve)
}

export default function formatRoutes(routes, Layout) {
  const formatRoutesArr = []
  routes.forEach(route => {
    const router = {
      meta: {}
    }
    const {
      pid,
      title,
      path,
      redirect,
      component,
      keep_alive,
      icon,
      name,
      children
    } = route
    if (component === 'Layout') {
      router['component'] = Layout
    } else {
      router['component'] = loadView(component)
    }
    if (redirect !== null) {
      router['redirect'] = redirect
    }
    if (icon !== null) {
      router['meta']['icon'] = icon
    }
    if (children && children instanceof Array && children.length > 0) {
      router['children'] = formatRoutes(children)
    }
    if (name !== null) {
      router['name'] = name
    }
    router['meta']['title'] = title
    router['path'] = path
    if (pid === 0) {
      router['alwaysShow'] = true
    }
    router['meta']['noCache'] = !keep_alive
    formatRoutesArr.push(router)
  })
  // 将404页面添加到最后面
  formatRoutesArr.push({ path: '*', redirect: '/404', hidden: true })
  return formatRoutesArr
}

后台返回的结构,看具体项目需求返回具体字段

[{
    pid: 0 / 1, // 用于判断是否固定显示在tab栏上
    title: '页面的title', // 用于显示页面的title
    path: '/页面的路径', // 用于访问的路径
    redirect: '/重定向路径', // 用于重定向
    component: '/页面对应的组件路径', // 前端告诉后台页面对应的项目路径
    keep_alive: true / false, // 页面是否缓存
    icon: '页面菜单栏显示的图标', // 前端提供给后台
    name: '页面名称',
    children: [
        {
            pid: 0 / 1, // 用于判断是否固定显示在tab栏上
            title: '页面的title', // 用于显示页面的title
            path: '/页面的路径', // 用于访问的路径
            redirect: '/重定向路径', // 用于重定向
            component: '/页面对应的组件路径', // 前端告诉后台页面对应的项目路径
            keep_alive: true / false, // 页面是否缓存
            icon: '页面菜单栏显示的图标', // 前端提供给后台
            name: '页面名称',
            children: []
        }
    ]
}]

3、获取处理后的菜单列表,动态注册

import router from './router'
// 获取储存在本地获取Vuex中的路由列表
const accessedRoutes = JSON.parse(localStorage.getItem('accessedRoutes') || {})
// 判断路由列表是否存在,不存在则需要重新获取
if (accessedRoutes && Object.keys(accessedRoutes).length) {
    router.addRoutes(accessedRoutes)
}

建议以上过程在路由拦截器中去实现

根据前端需要的信息,从后台返回信息中完全筛选出我们需要的信息,比如一些为值null的属性,不希望这些属性出现在最后的路由表中,好像只能用这种笨方法了,其实再在forEach嵌一个for循环也差不多,都要判断。敲黑板!这里要注意的就是,在导入组件的时候最好不要使用import()导入,会出很多问题的,导入的时候也要多写一层路径,例如千万不要把require(['@/views/${component}'], resolve)写成require(['@/${component}'], resolve)