约定式路由的菜单数据生成方案
在开发管理端系统时,基于页面目录生产的路由的routes是一个比较流行和省事的方案,但是,能不能再省点事,随便把菜单数据也一并生成呢?
基于这个问题,本文主要介绍CookJS,它可以理解成Vue CLI,是OPPO内部自研可插拔的企业级 Vue@ 应用框架,提供了构建、编码、打包等功能。
1. 目录与路由
约定式路由,也叫文件路由,就是不需要手写配置,文件系统即路由,通过目录和文件及其命名分析出路由配置。目前许多开源框架都支持,比如:UmiJS、Nuxt.js、vue-auto-routing等等。
CookJS参考了Nuxt.js的约定式路由规范,内置支持了约定式路由。下面讲解CookJS的约定式路由的一些概念。
1.1 基础路由
假设 pages 的目录结构如下:
.├── index.vue└── user ├── id.vue └── index.vue那么,CookJS 自动生成的路由routes.js,内容如下:
[ { path: '/user', name: 'user-index', component: () => import('@/pages/user/index.vue'), },{ path: '/user/id', name: 'user-id', component: () =>import('@/pages/user/id.vue') },{ path: '/', name: 'index', component: () => import('@/pages/index.vue'), }]1.2 动态路由
在 CookJS 里面定义带参数的动态路由,需要创建对应的以下划线作为前缀的 Vue 文件或 目录。
将:pages/user/id.vue 改名为:pages/user/_id.vue
.├── index.vue└── user ├── _id.vue └── index.vue生成对应的路由配置表为:
// routes[ { path: '/user', name: 'user-index', component: () => import('@/pages/user/index.vue'), },{ path: '/user/:id', name: 'user-id', component: () =>import('@/pages/user/id.vue') },{ path: '/', name: 'index', component: () => import('@/pages/index.vue'), }]1.3 嵌套路由
可以通过 vue-router 的子路由创建 cookjs 应用的嵌套路由。
创建内嵌子路由,你需要添加一个 Vue 文件,同时添加一个与该文件同名的目录用来存放子视图组件。
增加一个 pages/user.vue 文件,目录结构如下:
.├── index.vue├── user│ ├── id.vue│ └── index.vue└── user.vue # template需包含router-view那么,生成对应的路由配置表为:
// routes[{ path: '/user', name: 'user', component: () => import('@/pages/user.vue'), children: [ { path: '', name: 'user-index', component: () => import('@/pages/user/index.vue'), },{ path: 'id', name: 'user-id', component: () => import('@/pages/user/id.vue'), } ] }, { path: '/', name: 'index', component: () => import('@/pages/index.vue'),}]1.4 实现总结
以目录中 pages/user.vue,作为说明示例:
.├── index.vue├── user│ ├── id.vue│ └── index.vue└── user.vuevue-router 中routes字段所需要的值,如下
| vue-router字段 | pages目录结构 |
|---|---|
| component | import('pages/user.vue') |
| path | "pages/user":文件路径(去掉.vue) |
| name | path.replace(\//g,'-') |
| children | user.vue单文件和user目录同名,即开户嵌套路由,user目录内容即为children字段的值,注意,user.vue中template要包含<router-view> |
2. 路由与菜单
菜单和路由都是一种树的结构,我们先来看看一个简单菜单数据格式:
[ { "index": "菜单ID", "title": "菜单名", "iconClass": "菜单icon", "sort": "菜单排序", "router": "vue-router的路由对象", "children": ['...'] }, '....']2.1 菜单和目录对应
目录和菜单的数据直接一一对应(未做文案定制的效果)
路由中字段的值都是可以通过目录的层级和文件命名推算,但是,菜单需要有一些定制化的内容,比如:菜单名、菜单Icon、排列顺序等,但是,这些信息如何存放呢?
存放在文件命名,文件命名会很长很怪,显然不合适,no
存放在文件内容中,并能通过特定格式解析出来结构,yes
菜单信息借助了CookJS扩展> 路由元信息,即是扩展 vue单文件组件中自定义代码块 <route-meta> 来存放菜单信息。
2.2 meta扩展
vue单文件组件除了<template>、<script>和<style>这些语法块,还支持自定义语法块的。
CookJS扩展的路由元信息,源码解析如何扩展 <route-meta> ,vue单文件组件(pages/index.vue)内容如下:
// 单文件组件扩展route-meta<route-meta> export default { $layout: { name: '首页', icon: 'co-icon-home', index: 0 } }</route-meta><template> <div class="wrap"> Hi ,CookJS!<br /> path: @/pages/index.vue </div></template><script> export default { name: 'PageIndex', }</script><style lang="scss" scoped> .wrap { display: flex;}</style>借助 vue-template-compiler 解析代码,具体代码如下:
然后将 解析自定义语法块的内容写进 .cook/router/meta/index.js文件中。最终被 .cook/router/routes.js 文件引用
完整的 vue-router 的完整数据结构:
[ { path: '/user', name: 'user-index', component: () => import('@/pages/user/index.vue'), meta:{ $layout: { // 菜单定制文案 name: '首页', icon: 'co-icon-home', index: 0, disabled: true //是否显示 } }}]2.3 菜单一对多
菜单和路由 一对一的关系,通过router中meta字段绑定就已经实现了。
一个菜单对应多个路由的这种关系是比较常见的场景,比如,从列表页进入详情页时,路由虽然变化,但是系统的菜单还要保持一样。(观察url和基础列表菜单)
菜单:基础列表对应了 /infoList/basic 和 /infoList/basic/hide这两个路由,即一个菜单对应两个路由。
CookJS通过嵌套路由并结合 route-mate的一对一的关系扩展,来实现菜单和路由的一对多的关系。
先来看看路由数据结构:
[ { path: 'basic', name: 'infoList-basic', component: () => import('@/pages/infoList/basic.vue'), meta: { $layout: { name: '基础列表', index: 2, registerMenu: { // 一对多实现的关键标识字段 defalut: 'index' } } }, children: [ { path: '', name: 'infoList-basic-index', component: () =>import('@/pages/infoList/basic/index.vue'), meta: {}, },{ path: 'hide', name: 'infoList-basic-hide', component: () => import('@/pages/infoList/basic/hide.vue'), meta: {}, } ]}]通过识别route-meta中 registerMenu 字段后,将整个嵌套的路由识别成一个菜单,这样,就实现一个菜单对应多个子路由,从而完成一对多的关系实现。从routes中的路由元信息中就可以提取出菜单数据了。
3. 总结
约定式路由和vue单文件组件扩展(route-meta)自定义语法块,实现一套菜单、路由和目录的规范,除了节省了编写路由的代码,更重要的是在这种清晰的目录组织和代码编写方式,直接通过URL就可以定位到页面代码,在开发查问题的时候,也就变得十分简单。
☆ END ☆
OPPO互联网技术团队招聘一大波岗位,涵盖C++、Go、OpenJDK、Java、DevOps、Android、ElasticSearch等多个方向,请点击这里查看详细信息及JD。
更多技术干货
扫码关注
OPPO互联网技术