前言
经常看到大型项目中使用webpack
中的require.context
来批量引入文件,最近刚好在写组件化 icon,用到了这个方法,写一篇 blog 来记录一下。
backup:大部分代码是基于 Vue 项目的。
require.context 是什么
这是一个 webpack 的 API,通过执行 require.context 函数来获取指定的上下文,主要用于实现自动化导入模块;一般会遍历指定文件夹下的指定文件,实现自动导入,省去了每次都要调用import
方法。
使用场景
通用的场景是这样的:当发现多个模块在一个文件夹下,而且需要机械式的写引入语句时,都可以使用;实际场景有动态引入路由,动态引入图标文件等等。
require.context 函数
看下面这段代码:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
/**
* 批量导入文件
* @param {string} directory 文件目录
* @param {bool} useSubdirectories 是否读取子目录
* @param {regular expression} regExp 正则式匹配文件名和后缀
* @return: {function} with 3 properties, resolve, keys, id
*/
let requireContextFunc = require.context(directory, useSubdirectories = true, regExp = /^\.\/.*$/);
typeof requireContextFunc === 'function'; // true
requireContextFunc = (req) => {
// req is an element of keys
return `req对应的一个文件的模块`;
// 通俗点说就是平常我们写js代码中export出来的东西
}
requireContextFunc.resolve = (req) => {
// req is an element of keys
return `req匹配到的文件的完整的相对路径`;
}
requireContextFunc.keys = () {
return `Array, 所有匹配到的文件名组成的数组`;
}
requireContextFunc.id = `String, 执行环境的id`;
上述代码基本讲清楚了返回值的属性,再次总结一下:
- 返回值是一个函数,函数参数是相对路径+文件名组成的字符串,该函数返回模块;
- 函数的其中一个属性是
resolve
函数,参数和第一点一样,该函数返回文件所在的完整路径; - 函数第二个属性
keys
函数,没有参数,调用后返回由文件相对路径+文件名,这样的字符串,组成了一个字符串数组,作为返回值; - 函数第三个参数我没怎么用到,是字符串,表示执行环境的 id
实例
全局组件
在组件化开发的过程中,往往会注册一些全局组件,方便后续其他的组件去引用,通常我们会一个个去写import
引用,然后一起注册为全局组件;当全局组件比较多的时候,这个过程会显得有些繁琐。
项目目录(该目录是测试用的,一般全局组件会放在 global 之类的文件夹下):
index.js
1
2
3
4
5
6
7
8
import Vue from 'vue';
let contexts = require.context('.', false, /\.vue$/); // 获取所有.vue文件
contexts.keys().forEach(component => {
// 获取组件模块,这里最好先把componentEntity打印出来看看
let componentEntity = contexts(component);
// 使用内置的组件名称 进行全局组件注册,这里要注意使用这个方法需要在全局组件内部,写过组件名name这个属性,否则是读不到的
Vue.component(componentEntity.name, componentEntity);
});
后续在main.js
引入index.js
就行了,甚至不需要做什么额外操作。
局部组件
最近在项目里也遇到了在局部需要引入比较多的组件,但这些组件只会引用一两次,局部注册并使用和全局有些区别:不再使用全局的Vue
构造函数了,注册方式也有点不一样。
index.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
const contexts = require.context('.', false, /\.vue$/);
// console.log(contexts);
let components = new Object();
contexts.keys().forEach(component => {
let componentEntity = contexts(component);
// console.log(componentEntity);
let name = component.slice(component.lastIndexOf('/') + 1, component.lastIndexOf('.'));
name = name.replace(/(.)-(.)/g, (p0, p1, p2) => {
return p1 + p2.toUpperCase();
});
// console.log(name);
components[name] = componentEntity;
});
// console.log(components);
export default components;
real-home.vue
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<template>
<div>
<home />
<home-copy />
<home-copy-home />
</div>
</template>
<script>
import components from './login/index';
export default {
name: 'realHome',
components: components
};
</script>
可以看到比起全局还是有些不一样的,首先我没有用.name
这个属性直接访问组件的名字,有时候不想写组件名或者漏写了,只取文件名,就好一些,当然不是很提倡不写组件名,但是不能约束别人也会每个组件都写,然后就是文件名用xxx-xx-xxxx
这种格式命名组件,所以要转换一下;index.js
输出的是一个对象,因为注册组件时需要的也是对象,对象形式如下:
1
2
3
4
5
{
'home': context,
'homeCopy': context,
'homeCopyHome': context
}
这里的 context 是组件的内容了,是个代称。在real-home.vue
文件里,引入index.js
后,还需要在components
里面注册,参考上面代码就可以了。
总结
总之就是介绍了一种使用webpack
批量导入文件的方法,上面两个例子都是引入.vue
文件进行后续的操作,其他格式的文件也是一样的引入方式,只要注意核心的context
是一个函数,调用这个函数就能获取想要的文件内容。