如何利用requireContext实现批量导入
# 前言
最近在做一个图标组件,需要批量导入图片,如下:
import "./icons/alipay.svg"
import "./icons/qq.svg"
import "./icons/wechat.svg"
2
3
webpack
中提供了require.context
可以实现这个自动批量导入功能。
# require.context 语法
require.context(directory,useSubdirectories,regExp)
directory:表示检索的目录
useSubdirectories:表示是否检索子文件夹
regExp:匹配文件的正则表达式,一般是文件名
# 批量导入代码
注:以下版本导入时,无返回值。
# javascript
版本
以下代码可以 递归 导入 icons
文件夹下所有的 svg
图片
function importAll(requireContext){
return requireContext.keys().map(requireContext)
}
2
3
使用时:
try {
importAll(require.context('./icons/', true, /\.svg$/));
})
} catch (error) {
// 注释的目的:防止在 test 环境下没有 require.context
// console.log(error)
}
2
3
4
5
6
7
# typescript
版本
步骤如下:
安装
@types/webpack-env
配置tsconfig.json
{ "compilerOptions": { ... "types": [ "webpack-env" ] }, ... }
1
2
3
4
5
6
7
8
9typescript
类型定义如下:function importAll(requireContext: __WebpackModuleApi.RequireContext){ return requireContext.keys().map(requireContext) }
1
2
3使用方法同
js
案例
# 初步分析
其实对这部分原理理解也不是很深,如果想要进一步了解 webpack
运行机制可以看《深入理解webpack的require.context》 (opens new window) 这篇文章。
注:以下为读取
svg
图片,使用sprite-svg-loader
进行转化。
上述给的 js
版本或是 ts
版本,过于抽象,等价实现如下:
const requireComponents = require.context("./icons", true, /\.svg$/);
requireComponents.keys().forEach(fileName => {
console.log("pic:", fileName);
console.log("module:",requireComponents(fileName));
})
2
3
4
5
打印如下:
pic: ./alipay.svg
module: Module {default: BrowserSpriteSymbol, __esModule: true, Symbol(Symbol.toStringTag): 'Module'}
pic: ./qq.svg
module: Module {default: BrowserSpriteSymbol, __esModule: true, Symbol(Symbol.toStringTag): 'Module'}
pic: ./wechat.svg
module: Module {default: BrowserSpriteSymbol, __esModule: true, Symbol(Symbol.toStringTag): 'Module'}
2
3
4
5
6
7
8
可以发现 pic
即为导入 svg
相对路径地址,而 module
则为由 webpack
处理过后的 module
。通过 __esModule
可知其为 ESModule
模块,还需通过 .default
进一步取出模块内容。
注:如
svg
是由file-loader
做处理的话,打印结果如下:
pic: ./alipay.svg
static/alipay.55c59806.svg
pic: ./qq.svg
/static/qq.74ed9feb.svg
pic: ./wechat.svg
/static/wechat.2d422b78.svg
2
3
4
5
6
7
8
此时,require.context
导入的实际上是一个路径地址。
上述已经拿到了 module
后,那如何导入这些模块呢?实际上 requireComponents(fileName)
这个语句即等价于导入。它是一个函数两个作用,在导入模块的同时,又将 module
内部信息输出,如下:
验证如下:只打印,让函数无返回值。
const requireComponents = require.context("./icons", true, /\.svg$/);
requireComponents.keys().forEach(fileName => {
//注:这里只打印,无需显式写 requireComponents(fileName) 也可以完成模块的导入。
console.log("module:",requireComponents(fileName));
})
2
3
4
5
也即,在 importAll
这个函数,无导出也是可以成立的,如下:
type requireContextType = __WebpackModuleApi.RequireContext;
function importAll(requireContext: requireContextType): void {
requireContext.keys().forEach(requireContext)
}
2
3
4
但是,在使用时只能以 import "./importAll"
的当时导入模块了。
# 导出拦截
导出变量时还需要考虑一个问题就是,导入的模块的语法规范(cjs
还是 ejs
),为了支持不同语法规范时,需要对代码做进一步修改:
function importAll(r){
return r.keys().map(f => r(f).default || r(f))
}
2
3
如果想要进一步控制导出的内容也是可以的,只需要魔改上述代码即可,举例:
以下代码为 yy ,感觉可以实现。
function importAll(r){
let images = {};
r.keys().forEach(f => {
// 进一步过滤文件
if(/正则语法/g.test(f)){
let obj = r(f).default || r(f);
// 比如说将读取到的 svg 里面全部改为
images.push({[svgName]:obj});
}
})
return images
}
const images = importAll(require.context('./images', false, '/\.png/'));
// 调用时:
<img src={images["0.png"]}>
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18