“我报名参加金石计划1期挑战——瓜分10万奖池,这是我的第 4 篇文章,点击查看活动详情”
文章内容:用 webpack 打包两个简单的模块,分析打包后的代码,也就是运行时代码。
代码准备
index.js
const sum = require('./sum')
console.log(sum(6, 9));
sum.js
module.exports = (a, b) => {
return a + b
}
webpack.config.js
const path = require('path')
module.exports = {
entry: './index.js',
output: {
path: path.resolve(__dirname, 'build')
},
mode: 'none'
}
// npx webpack --entry ./index.js --output-path build --mode=none
在控制台执行
npx webpack
在目录下输出 build/main.js
/******/ (() => { // webpackBootstrap
/******/ var __webpack_modules__ = ([
/* 0 */,
/* 1 */
/***/ ((module) => {
module.exports = (a, b) => {
return a + b
}
/***/ })
/******/ ]);
/************************************************************************/
/******/ // 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__ = {};
// This entry need to be wrapped in an IIFE because it need to be isolated against other modules in the chunk.
(() => {
const sum = __webpack_require__(1)
console.log(sum(6, 9));
})();
/******/ })()
;
接下来对 main.js 的代码进行分析
运行时代码分析
整体分析
main.js 整个是一个立即执行函数,首先先看一下整体
立即执行函数内包裹了五个部分的内容:
- 1、2、4 是变量的声明与赋值,
- 3 是函数的声明,
- 5 又是一个立即执行函数,
这时还不知道这些变量和函数的作用是什么,可以带着疑问去调试代码;
接下来就进行断点调试
- 将断点打在 5 这个立即执行函数内部第一行代码上
- 然后打开
JavaScript Debug Terminal
,执行node main.js
- 进入我们所打的断点位置
- 然后单步调试,看代码的执行情况
通过调试运行时代码分析得出
- 1 ->
__webpack_modules__
:是一个数组,存放所有加载到的模块。在此示例中,__webpack_modules__[0]
是空的,__webpack_modules__[1]
是 sum.js 模块; - 2 ->
__webpack_module_cache__
:是一个对象,对模块执行结果进行缓存,这样能够保证每个模块只被执行一次; - 3 ->
__webpack_require__
:是一个函数,作用是加载模块,如果模块第一次被加载,则通过__webpack_modules__[moduleId]
匹配上对应的模块,并进行缓存;如果是已加载的模块,则直接从__webpack_module_cache__[moduleId]
取; - 4 ->
__webpack_exports__
:是一个对象,存放导出的内容; - 5 -> IIFE:加载入口模块
__webpack_require__
具体做了什么?
webpack 的模块加载器是如何实现的,见注释
/******/ (() => { // webpackBootstrap
/******/ var __webpack_modules__ = ([
/* 0 */,
/* 1 */
/***/ ((module) => {
module.exports = (a, b) => {
return a + b
}
/***/ })
/******/ ]);
/************************************************************************/
/******/ // The module cache
/******/ var __webpack_module_cache__ = {};
/******/
/******/ // The require function
/******/ function __webpack_require__(moduleId) {
/******/ // (1)检查模块是否存在于缓存中
/******/ var cachedModule = __webpack_module_cache__[moduleId];
// (2)如果缓存存在,则直接返回缓存中 moduleId 所对应的 exports 对象
/******/ if (cachedModule !== undefined) {
/******/ return cachedModule.exports;
/******/ }
/******/ // (3)如果缓存不存在,则创建一个 module 对象,用于初始化 module 信息,同时创建对应的缓存
/******/ var module = __webpack_module_cache__[moduleId] = {
/******/ // no module.id needed
/******/ // no module.loaded needed
/******/ exports: {}
/******/ };
/******/
/******/ // (4)通过 moduleId 匹配到 __webpack_modules__ 存放的对应的模块,并传入 module, module.exports, __webpack_require__ 参数,执行模块
/******/ __webpack_modules__[moduleId](module, module.exports, __webpack_require__);
/******/
/******/ // (5)返回 module.exports
/******/ return module.exports;
/******/ }
/******/
/************************************************************************/
var __webpack_exports__ = {};
// This entry need to be wrapped in an IIFE because it need to be isolated against other modules in the chunk.
(() => {
const sum = __webpack_require__(1)
console.log(sum(6, 9));
})();
/******/ })()
;
如何理解 5 -> IIFE:加载入口函数
将 main.js 改造一下
/******/ (() => { // webpackBootstrap
/******/ var __webpack_modules__ = ([
/* 0 */
() => {
const sum = __webpack_require__(1)
console.log(sum(6, 9));
},
/* 1 */
/***/ ((module) => {
module.exports = (a, b) => {
return a + b
}
/***/ })
/******/ ]);
/************************************************************************/
/******/ // 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__ = {};
// This entry need to be wrapped in an IIFE because it need to be isolated against other modules in the chunk.
__webpack_require__(0);
/******/ })()
;
可见 webpack
运行时代码最后是 __webpack_require__(0)
小结
通过对 webpack 运行时代码进行调试以及分析,如果不考虑缓存的情况,可用一句话说明其逻辑:加载入口模块,入口模块所依赖的其他模块,通过 __webpack_require__
模块加载函数进行递归加载存放在 __webpack_modules__
的对应模块。