使用 webpack API require.context 优化满屏的 import

185 阅读2分钟

摘要:在开发的时候有时候会遇到单文件大量引入文件的场景,如router.config中,需要引入的文件较多,并不是很友好,通过webpack的 APIrequire.context() 可以实现自动化导入模块,进而简化这一操作。
案例:如下的文件中引入大量图片(当然这不是个好的案例,此处先不考虑这些) image.png 优化之后的效果: image.png

1. 带表达式的 require 语句

注意:这里的require并非CommonJS中的require,而是 webpack 提供的特定的方法。区别如下: image.png

如果 require 中含有表达式,由于编译时并不清楚 具体 导入了哪个模块,因此会创建一个上下文。

🌰 举个栗子,有如下目录

  assest
  │
  └───images
  │   │   png1.svg
  │   │   png1.svg
  │   │
  │   └───icons
  │       │   icon1.svg
  │       │   icon2.svg

也就是说在执行

  require('./assets/images/' + name + '.svg');

的时候,webpack 会解析 require() 调用,然后提取出如下一些信息

  Directory: ./assets
  Regular expression: /^.*\.svg$/

1.1 上下文环境

在解析 require()的过程中,会创建一个上下文环境。它包含 对该目录下所有模块 的引用,可以使用匹配正则表达式的请求来导入这些模块。上下文模块中存在一个映射,该映射用于将请求转换为模块 ID:

  {
    "./png1.svg": 0,
    "./png2.svg": 1,
    "./icons/icon1.svg": 2
    "./icons/icon2.svg": 3
  }

2. require.context

require.context(directory, useSubdirectories = true, regExp = /^\.\/.*$/)函数可以实现自定义{{embed 上下文环境}}。 参数说明:

  • directory:要搜索的目录
  • useSubdirectories:是否还搜索其子目录
  • regExp:匹配文件的正则表达式
    🌰 示例:
  require.context('./test', false, /\.test\.js$/);
  // 创建一个上下文,其中文件直接来自 test 目录,require 包含的表达式以 `.test.js` 结尾。

  require.context('../', true, /\.stories\.js$/);
  // 创建一个上下文,其中文件来自父文件夹及其所有子级文件夹,require 包含的表达式以 `.stories.js` 结尾。

注意: 传递给 require.context 的参数必须是字面量!

2.1 上下文模块 API

上下文模块(require.context())会返回:接收一个 request 参数的 require 函数,如下:

image.png 使用Object.getOwnPropertyNames() 可以看到此函数有三个属性:resolvekeys 与 id

image.png 属性值说明: resolve 是一个函数,它返回 request 被解析后得到的模块 id。 keys 也是一个函数,它返回一个数组,该上下文环境查到的所有文件名字组成的内容:

image.png

3. 组织自动引入的内容实现工程化

这里以引入图片做为案例,路由文件类似:

  1. 定义工具函数
  export function importAll<T>(requireContext, fileType): T {
    const fileMap: Partial<T> = {};
    requireContext.keys().forEach((key) => {
      const regex = new RegExp(fileType, 'g');
      const fileName = key.replace(/(\.)|(\/)/g, '').replace(regex, '');
      fileMap[fileName] = requireContext(key);
    });
    return fileMap as T;
  }

  1. 在文件目录定义导出函数
  // /assets/homeIcon/index.ts
  import { importAll } from '@/utils/importFile';

  const context = require.context('./', false, /^.\/\w+\.png$/);

  // 定义文件名类型,在别处引用时能自动给出提示
  type FileNames = 'icon1' |
    'icon2' |
    'icon3' |
    'icon4' |
    'icon5';
  type FileObject = {
    [Prop in FileNames]: string
  }

  const fileMap = importAll<FileObject>(context, 'png');

  export default fileMap;

  1. 在引入文件中使用
  import homeIcon from '@/assets/homeIcon';

  ...
  <img src={homeIcon.icon1} alt="" />
  <img src={homeIcon.icon2} alt="" />
  <img src={homeIcon.icon3} alt="" />