小知识,大挑战!本文正在参与“程序员必备小知识”创作活动。
本文同时参与 「掘力星计划」 ,赢取创作大礼包,挑战创作激励金
动态路由(动态菜单)的开发规范
场景:因各个系统的菜单权限管理不同,导致后期维护的难道较高。因菜单权限的管理流程是一致的,所以我们需要用好框架本身自带功能的优势来开发
前提:使用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)