# require.context 的使用

# 使用方式

官方文档说法:

It allows you to pass in a directory to search, a flag indicating whether subdirectories should be searched too, and a regular expression to match files against.

意思是:该方法允许你传递一个文件夹地址,一个指明是否搜索子目录的 布尔值以及要匹配的文件的正则表达式。

所以,require.context 的方法调用姿势应该这样的:

const webpackContext = require.context(path, 是否搜索子目录, 正则表达式);
参数名称 备注
path 指明相对于该脚本的文件夹路径
是否搜素子目录 当设定的path还有子目录,如果为 false,则子目录的内容不会被加载
正则中表达式 要匹配的文件格式,比如要匹配以 .svg 结尾的图标,就是 /\.svg$/

# 全局示例

假设当前有文件夹结构如下:

.
├── index.js # 这里做 svg 文件的统一引入
└── svg # svg 图标文件夹
    ├── eye-open.svg
    ├── eye.svg
    └── user.svg
    └── ...(更多svg)

当在 index.js 中使用 require.context() 方法加载 svg 文件中的文件时,写法如下:

// 查找相对与 index.js 的 svg 目录下以 .svg 结尾的文件,
// 不搜索 svg 文件夹下的子目录
let webpackContext = require.context("./svg", false, /\.svg$/);

// 通过这一步就将所有的 svg 文件的 es module进行了导入
// 当该文件被引入像 vue 的 main.js 中之后
// 就进入了 webpack的依赖模块被相关的 loader 进行了打包处理了
webpackContext.keys().forEach((path) => webpackContext(path));

# webpackContext

从此处开始,我们将 require.context() 函数调用后的返回值称为 webpackContext

webpackContext 本身就是一个函数,它可以接收一个参数,官网称为 request。 个人理解】 对于 request 参数个人理解是要请求的模块在当前上下文中的路径信息。比如上面的 svg 文件夹下的文件 user.svg 文件,在当前上下文环境中(svg 文件夹下)的路径信息就是 ./user.js

webpackContext 有三个属性:keys函数resolve函数id属性

属性名 作用
id 上下文模块的 id, 比如当前的 id 就是 ./src/assets/icons/svg sync \\.svg$
resolve函数 返回已解析请求的模块 id
keys函数 返回的该模块可以处理的所有可能请求的模块的数组,简单一点就是满足搜索条件的模块

单独从文档来看,是不会理解这些属性都在干嘛,来看下 webpackContext 的源码部分:




















 
 
 
 
 
 
 

var map = {
	"./eye-open.svg": "./src/assets/icons/svg/eye-open.svg",
	"./eye.svg": "./src/assets/icons/svg/eye.svg",
	"./tree.svg": "./src/assets/icons/svg/tree.svg",
	"./user.svg": "./src/assets/icons/svg/user.svg"
};

function webpackContext(req) {
	var id = webpackContextResolve(req);
	return __webpack_require__(id);
}
function webpackContextResolve(req) {
	if(!__webpack_require__.o(map, req)) {
		var e = new Error("Cannot find module '" + req + "'");
		e.code = 'MODULE_NOT_FOUND';
		throw e;
	}
	return map[req];
}
webpackContext.keys = function webpackContextKeys() {
	return Object.keys(map);
};

webpackContext.resolve = webpackContextResolve;
module.exports = webpackContext;
webpackContext.id = "./src/assets/icons/svg sync \\.svg$";

从代码可以看到,开头就是目前我们引入的 svg 图标的映射关系,key 值是相对于 ./svg(在调用 require.context 方法时传入的 path) 的相对路径。value 值是相对于根目录的全路径信息(注意它们都是以根目录为基准,从 ./src 开始的文件路径,因为 webpack 处理文件默认就是从 src 文件下开始)。

接着就是各种函数的定义和对应属性的暴露了。

# webpackContext.keys()

// 源码部分
webpackContext.keys = function webpackContextKeys() {
  return Object.keys(map);
};
// 查找相对与 index.js 的 svg 目录下以 .svg 结尾的文件,
// 不搜索 svg 文件夹下的子目录
let webpackContext = require.context("./svg", false, /\.svg$/);

webpackContext.keys().forEach((path) => {
  console.log(path);
});

就这一块,很明显就是获取相对上下文环境 ./svg 的文件路径,返回值就是一个普通的数组。 An Image

# webpackContext.resolve()



 













// 源码部分
function webpackContext(req) {
	var id = webpackContextResolve(req);
	return __webpack_require__(id);
}

// 这个函数就是暴露出去的 webpackContext.resolve 函数
function webpackContextResolve(req) {
	if(!__webpack_require__.o(map, req)) {
		var e = new Error("Cannot find module '" + req + "'");
		e.code = 'MODULE_NOT_FOUND';
		throw e;
	}
	return map[req];
}

这个函数在 webpackContext 函数中调用了,而webpackContext 就是我们调用 require.context() 函数后最终获取的 webpackContext

去掉 webpackContextResolve 函数中抛出错误的部分,就是获取 map 映射的 value 值了。

所以,可以推测webpackContext 作为函数本身要接收的所谓request参数,其实就是keys()函数中的每一项,也就是上下文(./svg)环境中匹配到的每一个svg 文件路径,比如 user.svg 就是 ./user.svg

这也验证了我上面的个人理解。

那么接下来呢?

说完了 webpackContext 的所有属性,最后再看一下 webpackContext 函数本身

function webpackContext(req) {
  var id = webpackContextResolve(req);
  return __webpack_require__(id);
}

它的函数体内只有两行,第一行是调用 requireContext.resolve() 函数。上面已经说过了,得到的 id 是加载模块相对于根目录从 src 文件夹开始的全路径信息(./src/xxx/xxx/xxx/yy.svg)。

最后这个路径传给了一个 __webpack_require__ 函数,那它是干什么的呢?请参考它 ٩(●˙▿˙●)۶…⋆ฺ

# 示例:vue 组件的自动化引入

An Image

const requireComponent = require.context(
  // 其组件目录的相对路径
  "./components/context",

  // 是否查询子目录
  false,

  // 匹配基础组件文件名的正则表达式
  /[A-Za-z]\w+\.(vue|js)$/
);

requireComponent.keys().forEach((filename) => {
  // 获取组件的配置
  const componentConfig = requireComponent(filename);
  // 获取组件的名称
  const componentName = componentConfig.default.name;
  // 注册全局组件
  Vue.component(
    componentName,
    // 如果这个组件选项是通过 `export default` 导出的,
    // 那么就会优先使用 `.default`,
    // 否则回退到使用模块的根。
    componentConfig.default || componentConfig
  );
});