[扩展] 不确定的动态依赖
场景
我们知道,下面情况下,webpack 是会将 a 模块打包到最终结果的。
js
if (Math.random() < 0.5) {
require("./a");
}
但是,如果是这种情况呢?具体的文件名根据不同的 dom 元素的 value 决定的,这样的打包情况就成为不确定的动态依赖了。
- 目录结构如下:
sh
utils
--a.js
--b.js
index.js
- 代码
js
//webpack不确定到底用户使用哪个模块,所以会将utils下面所有的文件都打包进打包结果中
const module = document.getElementById("txt").value;
if (Math.random() < 0.5) {
require("./utils/" + module);
}
- 打包结果:
js
"./src/utils/a.js":xxx
"./src/utils/b.js":xxx
"./src/index.js":xxx
而上面 webpack 在执行 require("./utils/" + module);
的时候,是自动使用了一个函数---require.context()
,而我们要介绍的就是这个函数。
require.context()
我们现在看下 require.context() 的用法。 //index.js
js
// 仅在webpack运行过程中有效
// 参数1:目录,哪个目录中的模块需要添加到打包结果
// 参数2:是否递归寻找子目录,如果为true,表示需要寻找子目录
// 参数3:正则表达式,凡是匹配的才会加入到打包结果
const context = require.context("./utils", true, /\.js$/);
此时,我们执行npx webpack
分析编译结果: 编译结果:
js
//我们发现上面的代码被webpack转换为这样的了,实际上就是转换为__webpack_require__函数。
__webpack_require__("./src/utils sync recursive \\.js$");
//上面__webpack_require__导入了一个模块,我们看编译结果下这个模块具体样子:
"./src/utils sync recursive":function(module, exports, __webpack_require__){
//webpack在这里可能导入的用法全部整合在一个对象中了{"key","文件指纹"}
var map = {
"./a.js": "./src/utils/a.js",
"./b.js": "./src/utils/b.js"
};
}
//根据这句代码可知,./src/utils sync recursive这个模块最后返回了一个函数(编译结果中webpackContext就是个函数),那就意味着require.context返回结果是一个函数
module.exports = webpackContext;
接下来我们看一下,返回的这个函数的用法:
js
//context("对应的上面map对象的key"),会把那个文件内容输出
const context = require.context("./utils", true, /\.js$/);
console.log(context("./a.js")); //输出"我是a模块"
我们再认识一个方法:
js
//将打包的哪些模块以数组的形式列举出来
console.log(context.keys()); //["./a.js","./b.js"]
灵活使用
以上我们知道了 webpack 是怎么打包不确定依赖的,也了解了 require.context()函数的用法。 那我们一般什么时候用得上呢?
- 需求: 我们假设 utils 目录现在做了一个完整的功能,我们想将这个模块给合并到一个文件中,整体导出。
sh
utils
--a.js
--b.js
--index.js 负责整合整个utils的模块
按照我们所学的知识,可能会在 utils/index.js 中这样
js
exports.a = require("./a");
exports.b = require("./b");
//这样是一个方法,但是有一个弊端:
1.如果某天`utils/a.js`名字更改为`/c.js`,我们就得手动修改
2.如果添加`utils/d.js`文件,我们就得手动在这个文件中添加`exports.c = require("./c");`
我们需要的是一个自动化的方案
解决 在 utils/index.js 中写一段整合整个 utils 的逻辑
js
const context = require.context("./", true, /\.js$/);
for (const key of context.keys()) {
console.log(key); //./a.js ./b.js ./index.js
if (key !== "./index.js") {
// 截取出文件名,用做导出的模块名
let filename = key.substr(2, 1);
exports[filename] = context(key); //导出模块,模块名和模块内容
}
}
在入口文件 index.js 中引入
js
const utils = require("./utils");
console.log("utils", utils);
//utils最终运行结果: { a: '我是a.js', b: '我是b.js' }
小结
我们以后可能会学到 vue-router 和 vuex,他们虽然是将模块拆开写的,但是整体都是一部分,知识拆分成模块方便维护。 这个知识有点难,属于扩展课程,了解即可。