面对工程中成百上千个模块,Webpack究竟是如何将它们有序地组织在一起,并按照开发者预想的顺序运行在浏览器上的呢?本篇文章将通过一个简单的示例。分析一下Webpack模块打包的原理。
// example.js
module.exports = {
add: function(a,b) {
return a + b;
}
}
// index.js
const example = require("./example.js");
const sum = example.add(5,6);
console.log("sum:",sum);
上面的代码经过Webpack打包后将会成为如下的形式(为了易读性,删除了些注释的代码):
(() => {
var __webpack_modules__ = ({
"./src/example.js": ((module) => {
eval("module.exports = {\r\n add: function (a, b) {\r\n return a + b;\r\n },\r\n};\r\n\n\n//# sourceURL=webpack://webpack-demo/./src/example.js?");
}),
"./src/index.js":((__unused_webpack_module, __unused_webpack_exports, __webpack_require__) => {
eval("const example = __webpack_require__(/*! ./example.js */ \"./src/example.js\");\r\nconst sum = example.add(5, 6);\r\nconsole.log(\"sum:\", sum);\r\n\n\n//# sourceURL=webpack://webpack-demo/./src/index.js?");
})
});
// The module cache
var __webpack_module_cache__ = {};
// The require function
function __webpack_require__(moduleId) {
// Check if module is in cache
var cachedModule = __webpack_module_cache__[moduleId];
if (cachedModule !== undefined) {
return cachedModule.exports;
}
// Create a new module (and put it into the cache)
var module = __webpack_module_cache__[moduleId] = {
// no module.id needed
// no module.loaded needed
exports: {}
};
// Execute the module function
__webpack_modules__[moduleId](module, module.exports, __webpack_require__);
// Return the exports of the module
return module.exports;
}
var __webpack_exports__ = __webpack_require__("./src/index.js");
})();
这是一个最简单的Webpack打包结果。但是已经可以清晰地展示出它如何将具有依赖关系的模块串联在一起的。
上面的打包结果分为以下几个部分。
- 最外层匿名函数。它用来包裹整个打包结果,并构成自身的作用域。
- __webpack_modules__对象。工程中所有产生了依赖关系的模块都会以key-value的形式放在这里。key可以理解为一个模块的id,由数字或者一个很短的hash字符串构成;value则是由一个匿名函数包裹的模块实体,匿名函数的参数赋予了每个模块导出和导入的能力。
- __webpack_module_cache__对象。每个模块只在第一次被加载的时候执行,之后其导出值就被存储到这个对象里面,当再次被加载的时候webpack会直接从这里取值,而不会重新执行该模块。
- __webpack_require__函数。对模块加载的实现,在浏览器中可以通过调用__webpack_require__(moduleId)来完成模块的导入。
- __webpack_exports__对象。它的作用是加载入口模块并返回导出值。
下面分析一下,打包的结果如何在浏览器中执行的。
- 在最外层匿名函数中初始化浏览器执行环境,包括定义__webpack_module_cache__对象、__webpack_require__函数等,为模块的加载和执行做一些准备工作。
- 加载入口模块。每个打包结果有且仅有一个入口哦模块,在上面的示例中,index.js是入口模块,在浏览器中会从它开始执行。
- 执行模块代码。如果执行到了module.exports则记录下模块的导出值;如果中间遇到require函数(即__webpack_require__),则会暂时交出执行权,进入__webpack_require__函数体内进行加载其他模块的逻辑。
- 在__webpack_require__中判断即将加载的模块是否存在于__webpack_module_cache__中。如果存在则直接取值,否则执行该模块的代码获取导出值。
- 所有依赖的模块都已经执行完毕,最后执行权又回到入口模块。当入口模块的代码执行完毕,也就意味着整个文件运行结束。
提示
通过分析,可以看出,代码运行的第三步和第四步是一个递归的过程。Webpack为每个模块创造了一个可以导出和导入的环境,但本质上并没有修改代码的执行逻辑,因此代码执行的顺序与模块加载的顺序是完全一致的,这也是Webpack模块打包额奥秘。
示例代码仓库
https://gitee.com/zero_79152105/webpack-vblog
原创文章,作者:ZERO,如若转载,请注明出处:https://www.edu24.cn/course/webpack-module-bundle.html