# 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
的文件路径,返回值就是一个普通的数组。
# 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 组件的自动化引入
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
);
});