前端优化 -- 使用 require.context 让项目实现路由自动导入

1,508 阅读4分钟

最近新接手了公司的两个项目,其实也是往项目里加入新功能,因为那是原本已经存在,而且已经运行了一段时间的项目,是标准vue-cli生成的所谓“vue 全家桶”项目。

然而,当我打开项目时,里面的代码(路由模块)是这样的:

也就是在一个index.js文件里写入了所有的路由配置...

当时,心里就一万个“草尼玛”奔腾而过...

根据经验,项目一定会越来越庞大,然后,如此无模块划分的架构设计显然对以后的维护会是个灾难。

于是乎,我想趁现在代码量还在可控范围(项目功能还不算太多)的时候赶紧优化一下,所以,去跟领导如此这般的说明的问题之后,得到了领导的同意,就行动起来了~

1、分模块:

首页,当然是要把不同模块的路由分离开来(本来想只把新加入的功能模块做处理,老模块保留现状,因为复制、粘贴也是很耗体力的。但是,想想所幸现在项目还不大,再加上目前虽然不年轻但还算力壮,且还稍微有点强迫症的催动下,所以还是决定将现在有代码拆开...),心里小小斗争一下之后,就开干了!于是,就有了这样的结构:

同时,index.js的全部代码浓缩成了这样:

为啥module目录里的文件会是.routes.js后缀呢?这个嘛...

其实是一个小技巧,并不是硬性规定:1.为了方便正则匹配容易识别; 2.为了标识文件的功能,让人一看就知道这文件是干嘛的!

啥正则匹配?

2、自动导入:

为啥能将index.js浓缩成这样呢?其实就是代码所示,利用require.context来实现自动导入...

require.context :是webpack提示的api,通过执行require.context函数遍历获取到指定文件夹(及其下子文件夹)内的指定文件,然后自动导入。

语法如下:

require.context(directory, useSubdirectories = false, regExp = /^.//);

该方法有三个参数:

参数名 作用
directory 读取的目录
useSubdirectories boolean值,表示是否遍历子目录
regExp 匹配文件的正则表达式(即你要读取目录下什么类型的文件,就是这个正则匹配)

require.context() 返回一个函数,该函数包含三个属性 resolve() keys() id

属性 作用
resolve 是一个函数,他返回的是被解析模块的id ,接受一个参数request, request为directory文件夹下面匹配文件的相对路径,返回这个匹配文件相对于整个工程的相对路径
keys 也是一个函数,他返回的是一个数组,该数组是由所有可能被上下文模块解析的请求对象组成
id 是上下文模块里面所包含的模块 id. 它可能在你使用 module.hot.accept 的时候被用到

我们的示例里用到的keys():返回匹配成功模块的名字组成的数组:

function importAll(r) {
    r.keys().forEach(...)
}

// r.keys() -> ['./action.routes.js', './apply.routes.js', './base.routes.js', './common.routes.js', './cost.routes.js', './liveActivity.routes.js']

可以看到拿到的就是module目录里的所有文件;

拿到数组文件之后便可对其进行forEach,然后通过r(key).default拿到文件的内容也就是各个模块写好的路由配置,从而也就实现了路由模块自动导入功能,从此,每次只需要将新加的路由命名为xxx.routes.js放入module目录(也可以是里面的子目录,因为第二个参数设置成了 true),也就不必每次加了新路由模块都手动import了!

同时,require.context,还能实现其他模块的自动导入功能,比如:Vue官网也提到实现基础组件的全局注册(Vue官网示例

所以为了以后全局注册组件方便,我也将相关功能加入到了项目中:

// 利用require.context 实现公共组件自动注册功能
const requireComponent = require.context('@/views/common/globalComponents', true, /[A-Z]\w+\.(vue|js)$/); // 不排除.js的render函数生成的组件
  requireComponent.keys().forEach(fileName => {
    // 获取组件配置
    const componentConfig = requireComponent(fileName);
    let lastSlash = fileName.lastIndexOf('/') + 1; // 最后一个斜杠的索引位置(支持匹配子目录下的文件)
    let fName = fileName.slice(lastSlash).replace(/\.\w+$/, '');
    let firstLetter = fName.slice(0, 1).toUpperCase();
    const remainLetters = fName.slice(1);
    // 获取组件的PascalCase(大驼峰)命名
    const componentName = firstLetter + remainLetters;
    Vue.component(
      componentName,
      // 如果这个组件选项是通过 `export default` 导出的,
      // 那么就会优先使用 `.default`,
      // 否则回退到使用模块的根。
      componentConfig.default || componentConfig
    )
  })

此文在我自己的博客园里也有写,现在决定转战到掘金了所以拿过来用用,抄自己的文章应该不算抄袭吧,哈哈...原来有写了一些错字,这里还稍微改正了一下! (要去看看?