菜单即路由,共享配置html
views
目录中存放业务组件;业务模块分文件夹存放,路由主组件使用index
命名存放在以路由名的目录下。vue
views **业务组件 └── base 业务分组目录,对应router目录中的菜单组 ├── about 业务功能 │ ├── index.vue 业务路由页面主组件 │ └── ...others.vue 相关的分块组件 ├── welcome │ └── index.vue └── index.vue 子路由组件,在菜单开启子路由时可用
每个配置文件对应一组菜单,文件名对应views下面的子目录,配置结构以下:webpack
// base.ts const menu: IMenu = { name: 'base', // 菜单名称,对应views目录下的子目录 title: '基本信息', // 菜单标题 isPath: true, // 是否为访问父路径,默认状况下菜单下的目录为一级路由,开启此项将读取目录下index.vue做为子路由组件,文件不存在时自动生成组件。 routes: [ // 菜单目录 { name: 'about', // 菜单名称,对应路由名称 title: '关于', // 菜单标题,对应路由meta信息 filename: 'about', // 组件文件名,默认为name path: 'about', // 访问路径,配置方式同vue-router,默认为name ...RouterConfig // 其它vue-router的路由配置 }, { name: 'welcome', title: '欢迎' } ] } export default menu
menuToRoute
方法将菜单配置转换为路由配置 web
参数vue-router
menu
:单个菜单配置对象或多个配置对象数组isRoot
:是否为根路由;路由配置中根路由path
须要以/
开头// menuToRoute.ts export default function formatRoute(menu: IMenu | IMenu[], isRoot?: boolean) { let _routes: RouteConfig[] = [] if (Array.isArray(menu)) { for (const item of menu) { _routes = _routes.concat(formatRoute(item, isRoot)) } return _routes } const { name: menuName, isPath, routes } = menu const children = routes.map(route => { const { name, filename = name, path, title, meta, ...config } = route return { name, path: (!isPath && isRoot ? '/' : '') + (path || name), component: () => // 使用chunks方法对代码按照菜单分块打包,详见后面说明 chunks(menuName, filename).then(e => { // 在使用keep-alive动态缓存include属性时须要组件name // 加上'v-'前缀避免“Do not use built-in or reserved HTML elements as component id” ;(e.default.options || e.default).name = 'v-' + name return e }), meta: title ? { title, ...meta } : meta ...config, } }) _routes = isPath ? [ { path: (isRoot ? '/' : '') + menuName, // 若是使用二级路由,读取菜单目录下的index为子路由组件,没有找到使用默认组件 component: () => chunks(menuName, 'index').catch(() => subView), children } ] : children return _routes }
与其它路由配置结合使用:typescript
// @/router/routes.ts import Home from '@/views/Home.vue' /* 导入全部菜单配置数组 */ import menu from './menu/' import menuToRoute from './menuToRoute' const ISROOT = true const routes = [ { path: '/', name: 'home', component: Home }, ...menuToRoute(menu, ISROOT) ] export default routes
路由对应的组件使用 webpack的import()
方法(查看官方文档)懒加载,可根据需求修改menuToRoute.ts
文件中的chunk
方法进行代码切割打包。api
通常小的项目中,能够直接使用
import( `@/views/${name}/index` )
就能够了,此方法将每一个目录下的index默认导出类型文件为入口将相关引用的文件进行打包;结合前面约定路由主组件以index命名的规范,能够避免将其它没有引用到的文件打包。
/** * menuToRoute.ts * 代码切割打包 * @param type 菜单组(文件夹)名称 * @param name 路由名称或路由对应文件名 */ function chunks(type: string, name: string) { switch (type) { case 'guide': // guide菜单下的路由若是没有指定filename,将默认导入md文件,因为md非默认导入类型,因此须要加上扩展名 if (!/(^index)|(\.\w+$)/.test(name)) { name += '.md' } return import(/* webpackChunkName: 'guide' */ `@/views/guide/${name}`) case 'example': // 将views/example/下的全部vue和tsx文件打成一个包 return import( /* webpackChunkName: 'example',webpackMode: "lazy-once",webpackInclude: /\.(vue|tsx)$/ */ `@/views/example/${name}` ) default: // 将views目录下(包括子目录)全部index文件的默认导入类型分块打包 return import( `@/views/${name}/index` ) } }
应用到elment-ui的NavMenu菜单导航组件示例:数组
<el-menu :default-active="$route.name" @select="$router.push({ name:$event })"> <el-menu-item index="home"> <template slot="title">首页</template> </el-menu-item> <el-submenu :index="menu.name" v-for="menu of menuList" :key="menu.name"> <template slot="title">{{ menu.title }}</template> <el-menu-item :index="item.name" v-for="item of menu.routes" :key="item.name"> {{ item.title }} </el-menu-item> </el-submenu> </el-menu>
权限过滤缓存
/** * @param menuList 后台配置的全部权限信息列表 * @param roleList 用户多个角色信息 **/ function buildUserRule (menuList, roleList) { // 合并角色权限列表 const menuIdList = [].concat(roleList.map(role=>role.menuIdList)) // 过滤生成用户权限 const rules = menuList.filter(item => menuIdList.includes(item.id)) // 经过权限信息中的id和parentId对应关系生成目录树结构 return formatTree(rules) }
将生成的权限树结构存放在store中,推荐格式以下:bash
{ 菜单名:{ // 对应菜单组 目录名:{ // 对应路由 tab名:{ // 路由页面中多个页签 edit: true // 操做按钮 delete: true } } } }
经过这种格式在菜单显示的时候方便进行权限过滤,进入到某个路由时也方便取到当前路由下的权限进行权限适配
原创文章,转载请声明,欢迎一块儿讨论! @nicefan.cn