今天通过一个例子,来看看webpack的一个编译结果的代码是怎么样的,它是如何来将模块化的代码合并成一个文件的。
项目初始化
我们还是一样,先创建好项目,初始化package.json,安装好webpack,
npm i -D webpack webpack-cli
package.json
{
"name": "test",
"version": "1.0.0",
"description": "",
"main": "index.js",
"dependencies": {
"jquery": "^3.7.1",
"webpack": "^5.91.0",
"webpack-cli": "^5.1.4"
},
"devDependencies": {},
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"build": "webpack --mode=production",
"dev": "webpack --mode=development --watch"
},
"author": "",
"license": "ISC"
}
接着我们创建src目录,里面创建两个文件,a.js,index.js
a.js
console.log("module a");
module.exports = "a";
index.js
console.log("index module");
const a = require("./a");
console.log(a)
然后我们创建dist目录,里面创建一个index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<script src="./main.js"></script>
</body>
</html>
接着我们执行npm run dev去打包
编译结果代码
我们把他编译结果里面的注释全部去掉
(() => { // webpackBootstrap
var __webpack_modules__ = ({
"./src/a.js":
((module) => {
eval("console.log(\"module a\");\r\nmodule.exports = \"a\";\n\n//# sourceURL=webpack://test/./src/a.js?");
}),
"./src/index.js":
((__unused_webpack_module, __unused_webpack_exports, __webpack_require__) => {
eval("console.log(\"index module\");\r\nconst a = __webpack_require__(/*! ./a */ \"./src/a.js\");\r\nconsole.log(a)\n\n//# sourceURL=webpack://test/./src/index.js?");
})
});
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] = {
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的模块
var __webpack_modules__ = ({
"./src/a.js":
((module) => {
eval("console.log(\"module a\");\r\nmodule.exports = \"a\";\n\n//# sourceURL=webpack://test/./src/a.js?");
}),
"./src/index.js":
((__unused_webpack_module, __unused_webpack_exports, __webpack_require__) => {
eval("console.log(\"index module\");\r\nconst a = __webpack_require__(/*! ./a */ \"./src/a.js\");\r\nconsole.log(a)\n\n//# sourceURL=webpack://test/./src/index.js?");
})
});
上面的代码,通过使用一个对象的方式
属性名为我们模块化的文件路径,属性值则是一个回调函数,这个函数里面通过eval来运行我们在模块化写的代码。
Tips:eval()函数是将字符串作为代码进行解析和执行,webpack使用eval,主要是便于我们进行一个调试,它会执行sourceURL,让我们开发遇到问题的时候可以知道是哪个具体的源文件出现了问题
require函数
我们再分析下面这段代码,首先__webpack_module_cache__是用来缓存我们导入过的模块的结果的一个对象
__webpack_require__是具体的require函数
大致的执行过程
1.检测是否存在模块缓存,如果存在就直接返回缓存结果,不存在就接着往下执行
2.创建一个模块对象,对象里面还有一个exports属性,也是一个对象,并且赋值给我们的缓存的结果,以及module变量
3.调用这个模块的函数,同时传递三个参数(module,module.exports,webpack_require)(也就是我们在模块里面写的代码)
4.返回module.exports
var __webpack_module_cache__ = {};
// The require function
function __webpack_require__(moduleId) {
// Check if module is in cache
//检查模块是否有缓存
var cachedModule = __webpack_module_cache__[moduleId];
//如果缓存不等于undefined,直接返回缓存结果
if (cachedModule !== undefined) {
return cachedModule.exports;
}
//创建一个对象,{exports:{}}
// Create a new module (and put it into the cache)
var module = __webpack_module_cache__[moduleId] = {
exports: {}
};
//调用模块的函数
// Execute the module function
__webpack_modules__[moduleId](module, module.exports, __webpack_require__);
//返回module.exports这个导出结果
// Return the exports of the module
return module.exports;
}
总结
经过分析上面的一个简单的代码的编译结果,我们可以知道webpack的最终编译的结果的代码,了解其中的原理。
- 它使用的立即执行函数,不会污染全局变量
- 它使用模块路径作为对象属性标识,这个属性名是唯一的
- 它将我们模块里面的代码通过函数的形式存储,eval形式执行,便于我们进行调试
- 它里面提供了一个require的函数,用来将我们的模块代码进行执行以及导出。以及它使用一个全局缓存对象将我们的模块导入结果进行了一次缓存
经过这次学习,对webpack 的一个运行的原理又有了一个新的认识,加油,继续深入学习webpack