Vue2中利用webpack动态生成路由
Vue2中利用webpack动态生成路由
在Vue2中,我们如果想要新增一个页面,步骤一般如下:
- 在
src/views
目录下新建路由组件page1.vue
; - 在
src/router
目录下新增路由配置,建立起映射关系;
这是最简单的情况,但是每次新增页面都要改动两处地方,着实不便,其次,随着项目越做越大,路由中的映射关系也会变得越来越复杂,超级难维护,比如说我现在接手的项目,路由组件就有180个,因为没有注释说明等原因,哪些路由还要哪些不要了也不知道,导致这个路由文件只能增不能减,体积也变的越来越大了。
路由配置文件不想维护的话,于是便想着将路由维护的工作转移到目录结构和路由组件本身上。
实现思路
首先是要导入某个目录,将目录中第一子层级的所有单文件组件作为一级路由,然后递归遍历,依次生成二级路由、三级路由等;
其次就是,并不是所有的单文件组件都是作为路由组件的,因此我们要新增一个属性用于区分,只有存在该属性的组件才会生成路由;
具体实现
require.context导入目录
想要导入某个目录,可以使用 webpack
中的 require.context()
方法,该方法接收4个参数
directory
要导入的目录的路径useSubdirectories
是否导入子目录,默认为true
regExp
正则匹配,可以通过这个控制要导入的文件类型,默认是全部类型mode
导入模式,异步导入还是同步导入,默认是同步sync
require.context()
会返回一个导出函数 require
,该函数可以接收一个参数 request
,导出函数存在3个属性
resolve
是一个函数,它返回 request 被解析后得到的模块 id。keys
也是一个函数,它返回一个数组,由所有可能被此 context module 处理的请求,其实就是路径字符串。
比如说,我把 src/views
目录下的 .vue
文件都导出了,可以像下面这样写
const r = require.context('../views', true, /\.vue/)
console.log(r.keys())
打印出 keys()
可以看到里面都是路径
对比我们的目录就更加明了了
接下来就是区分哪些组件是路由组件,哪些是非路由组件了,我们可以给路由组件增加一个 $dynamicRoute
属性,里面存放一些我们配置路由需要的参数,比如说 path/name/meta
等等
$dynamicRoute: {
name: "IamModule1",
path: "IamModule1",
meta: { description: "模块1" },
},
存在这个属性的组件才会动态生成路由,这样的话,我们对路由的维护就全部集中到了组件文件身上了。
然后根据上面的目录,我们给除了 component1.vue
文件之外的所有 .vue
文件都加上这个属性,然后开始继续完善代码
name和path的默认值
一个路由配置对象,一定会有 path
然后建议也有 name
,而且 path
和 name
在同级路由中还不能重复,因此我们要想想假如我们没有配置这两个属性的时候,该怎么给它们赋予一个默认值呢?
首先,在同一目录下不会存在相同名称的文件,也就是说,在同一目录下,文件名是唯一的,因此我们可以把 keys()
方法中的元素作为 name
和 path
的初始值,而且这样也容易记,只需要看目录结构就能知道了。因此我们需要一个转换函数来把路径转换成我们需要的具有一定格式的字符串
/**
* 将路径转换成name/path
* @param {string} path
*/
function path2name(path) {
if (typeof path !== 'string') return ''
let name = ''
path = path.split('/')
path.shift()
name = path.join('_').replaceAll('.vue', '').toUpperCase()
return name
}
上面这个方法会将 ./module1/fun1/index.vue
这个字符串转换成 MODULE1_FUN1_INDEX
这种形式,我们把转换结果用作我们的 name
和 path
即可。
生成路由配置项
我们还还需要一个方法 routeItemGenerator
来生成路由配置项,该方法接收两个参数
key
就是路径字符串component
就是路由组件
然后我们会通过这两个参数,来生成一个具有 name/path/meta/component
的路由对象
/**
*
* @param {*} key
* @param {*} config
*/
function routeItemGenerator(key, component) {
const config = component.$dynamicRoute
const route = {}
//路由的name
if (config.name) {
route.name = config.name
} else if (component.name) {
route.name = component.name
} else {
route.name = path2name(key)
}
// 路由的path
route.path = config.path || route.name
// 元数据
route.meta = config.meta || { requiresAuth: true }
// 路由组件与路由映射
route.component = component
// 返回
return route
}
通过上面的代码我们可以看出,路由配置项中的 name
属性默认会先去 $dynamicRoute
中取,如果取不到,就用组件的 name
,还是没有的话就使用组件所在的路径作为 name
,然后路由的 path
则是先判断 $dynamicRoute
是否存在,不存在则使用 name
属性。
完整代码
整个功能的完整代码如下
const r = require.context('../views', true, /\.vue/)
const routes = []
r.keys().forEach(key => {
console.log(key)
const component = r(key)
// 判断是否存在 $dynamicRoute 属性 存在才作为路由
if (component.hasOwnProperty('$dynamicRoute')) {
routes.push(routeItemGenerator(key, component))
}
});
/**
*
* @param {*} key
* @param {*} config
*/
function routeItemGenerator(key, component) {
const config = component.$dynamicRoute
const route = {}
//路由的name
if (config.name) {
route.name = config.name
} else if (component.name) {
route.name = component.name
} else {
route.name = path2name(key)
}
// 路由的path
route.path = config.path || route.name
// 元数据
route.meta = config.meta || { requiresAuth: true }
// 路由组件与路由映射
route.component = component
// 返回
return route
}
/**
* 将路径转换成name/path
* @param {string} path
*/
function path2name(path) {
if (typeof path !== 'string') return ''
let name = ''
path = path.split('/')
path.shift()
name = path.join('_').replaceAll('.vue', '').toUpperCase()
return name
}
/**
* 生成路径树
* @param {string} path 待解析路径 格式 './xx/x/xx.xx'
* @param {map} tree 已存在的路径树
*/
function pathTreeGenerator(path, tree) {
}
export default routes
因为目前而言,我这个项目只需要用到一级路由,而不需要子路由,因此不对路径做嵌套分析,如果需要的话,只要实现 pathTreeGenerator
方法即可。
上面这个文件会导出一个路由配置数组,我们把这个数组合并到路由配置数组中即可了。
输入路由,跳转成功,说明可以用。
转载自:https://juejin.cn/post/7207269389472677945