webpack require.context使用

Posted by Youzi on August 19, 2020

前言

经常看到大型项目中使用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`;

上述代码基本讲清楚了返回值的属性,再次总结一下:

  1. 返回值是一个函数,函数参数是相对路径+文件名组成的字符串,该函数返回模块;
  2. 函数的其中一个属性是resolve函数,参数和第一点一样,该函数返回文件所在的完整路径;
  3. 函数第二个属性keys函数,没有参数,调用后返回由文件相对路径+文件名,这样的字符串,组成了一个字符串数组,作为返回值;
  4. 函数第三个参数我没怎么用到,是字符串,表示执行环境的 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是一个函数,调用这个函数就能获取想要的文件内容。