webpack 打包文件分析
- @Author: kevinlaizhiyu@foxmail.com|
- @Date: 2025-02-08
- @Description: webpack 打包文件分析
阅读代码准备
// ./sub.js
export default function sub(a, b) {
return a - b;
}
// ./index.js
import sub from "./sub";
const res = sub(1, 2);
console.log(res);
// ./webpack.config.js
const path = require("path");
module.exports = {
entry: "./index.js",
output: {
path: path.resolve(__dirname, "dist"),
filename: "bundle.js",
},
};
npm install webpack webpack-cli -D # 安装依赖
npm run build # 构建
打包后的代码(已删除部分注释)
// bundle.js
(() => {
// webpackBootstrap
"use strict";
var __webpack_modules__ = {
"./src/index.js": (
__unused_webpack_module,
__webpack_exports__,
__webpack_require__
) => {
__webpack_require__.r(__webpack_exports__);
var _sub__WEBPACK_IMPORTED_MODULE_0__ =
__webpack_require__("./src/sub.js");
const res = (0, _sub__WEBPACK_IMPORTED_MODULE_0__["default"])(10, 5);
console.log("**** res ****", res);
},
"./src/sub.js": (
__unused_webpack_module,
__webpack_exports__,
__webpack_require__
) => {
__webpack_require__.r(__webpack_exports__);
__webpack_require__.d(__webpack_exports__, {
default: () => Sub,
});
function Sub(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;
}
/* webpack/runtime/define property getters */
(() => {
// define getter functions for harmony exports
__webpack_require__.d = (exports, definition) => {
for (var key in definition) {
if (
__webpack_require__.o(definition, key) &&
!__webpack_require__.o(exports, key)
) {
Object.defineProperty(exports, key, {
enumerable: true,
get: definition[key],
});
}
}
};
})();
/* webpack/runtime/hasOwnProperty shorthand */
(() => {
__webpack_require__.o = (obj, prop) =>
Object.prototype.hasOwnProperty.call(obj, prop);
})();
/* webpack/runtime/make namespace object */
(() => {
// define __esModule on exports
__webpack_require__.r = (exports) => {
if (typeof Symbol !== "undefined" && Symbol.toStringTag) {
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
}
Object.defineProperty(exports, "__esModule", { value: true });
};
})();
// startup
// Load entry module and return exports
// This entry module can't be inlined because the eval devtool is used.
var __webpack_exports__ = __webpack_require__("./src/index.js");
})();
代码解读
1. 立即执行函数 (IIFE):魔法城堡的大门
整个 bundle.js 文件是一个立即执行函数(IIFE),用于执行 Webpack 的构建逻辑。这确保了所有的模块和变量都在一个封闭的作用域中,避免污染全局命名空间。
(() => {
// webpackBootstrap
"use strict";
// ...
})();
比喻:想象你站在一座宏伟的魔法城堡前,这座城堡是 Webpack 的魔法工厂。当你推开这扇巨大的门(自执行函数),你就进入了这个充满魔法的世界。这扇门确保所有的魔法都在一个安全的环境中进行,不会影响外界。
意义:使用 IIFE 确保所有模块和变量都在一个封闭的作用域中,避免污染全局命名空间。这样可以防止与其他代码冲突,并提供更好的封装性。
2. 模块集合 (__webpack_modules__):魔法配方库
__webpack_modules__ 变量表示所有模块的集合,键为模块的 id,值为一个函数,表示模块的加载函数。每个模块的定义被包装成一个函数,以便在需要时调用
var __webpack_modules__ = {
"./src/index.js": (
__unused_webpack_module,
__webpack_exports__,
__webpack_require__
) => {
// 模块内容
},
// 其他模块...
};
比喻:在城堡的深处,有一个巨大的魔法配方库(__webpack_modules__)。每个模块都有自己的独特配方,比如 ./src/index.js 和 ./src/sub.js。这些配方告诉 Webpack 如何制作每个模块,最终将它们组合成一个完整的魔法药剂(你的应用程序)。
意义:将所有模块的定义集中存储在一个对象中,方便 Webpack 在需要时查找和加载模块。每个模块被包装成一个函数,确保模块的独立性和可复用性。
3. 模块缓存 (__webpack_module_cache__):魔法仓库
__webpack_module_cache__ 变量表示所有模块的缓存,键为模块的 id,值为对象,包含模块的导出。这确保了已经加载过的模块不会重复加载,提高了性能。
var __webpack_module_cache__ = {};
比喻:接下来是一个巨大的魔法仓库(__webpack_module_cache__),用来存放已经处理过的模块。每次你调用 __webpack_require__ 函数时,Webpack 都会先检查这个仓库,看看是否已经有了这个模块。如果有,就直接返回;如果没有,就会创建一个新的模块并存入仓库。这样可以避免重复劳动,提高效率。
意义:缓存已经加载的模块,避免重复加载,提高性能。这对于大型项目尤其重要,可以显著减少不必要的计算和资源消耗。
4. 模块加载函数 (__webpack_require__):魔法传送门
__webpack_require__ 函数表示模块的加载逻辑。它会检查模块是否已经在缓存中,如果存在则直接返回缓存的结果;否则,它会执行模块的定义函数,并将结果存入缓存。
function __webpack_require__(moduleId) {
var cachedModule = __webpack_module_cache__[moduleId];
if (cachedModule !== undefined) {
return cachedModule.exports;
}
var module = (__webpack_module_cache__[moduleId] = {
exports: {},
});
__webpack_modules__[moduleId](module, module.exports, __webpack_require__);
return module.exports;
}
比喻:__webpack_require__ 就像一个魔法传送门,你可以通过它瞬间到达你需要的任何模块。每次你调用这个函数,Webpack 都会检查它的仓库(__webpack_module_cache__),看看是否已经有了这个模块。如果有,就直接返回;如果没有,就会创建一个新的模块并存入仓库。这个过程就像是传送门带你去到不同的房间,每个房间都是一个模块。
意义:提供一个统一的接口来加载模块,确保模块的加载顺序和依赖关系正确。通过缓存机制,提高了模块加载的效率。
5. 工具函数:魔法助手
Webpack 提供了一些辅助函数来处理模块的导出和属性检查:
__webpack_require__.r:魔法标志
在 exports 中定义 __esModule 属性,值为 true ,以确保模块可以正确地使用 ES 模块语法。
__webpack_require__.r = (exports) => {
if (typeof Symbol !== "undefined" && Symbol.toStringTag) {
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
}
Object.defineProperty(exports, "__esModule", { value: true });
};
比喻:__webpack_require__.r 是一个魔法标志,确保每个模块都有一个特殊的标签(__esModule),以便其他模块知道如何正确地使用它。就像给每个魔法药剂贴上标签,确保使用者知道这是什么药剂以及如何使用。
意义:确保模块导出的内容符合 ES 模块规范,使得其他模块可以正确识别和使用这些导出。
__webpack_require__.d: 魔法挂载器
在 exports 上挂载要导出的方法或变量。
__webpack_require__.d = (exports, definition) => {
for (var key in definition) {
if (
__webpack_require__.o(definition, key) &&
!__webpack_require__.o(exports, key)
) {
Object.defineProperty(exports, key, {
enumerable: true,
get: definition[key],
});
}
}
};
比喻:__webpack_require__.d是一个魔法挂载器,负责将模块的导出内容挂载到 exports 上。它像一个魔法挂钩,把模块的功能一个个挂上去,确保其他模块可以方便地访问这些功能。
意义:在 exports 上挂载要导出的方法或变量,确保模块的导出内容可以在外部被正确引用和使用。
__webpack_require__.o: 魔法检测器
判断对象是否包含指定的属性
__webpack_require__.o = (obj, prop) =>
Object.prototype.hasOwnProperty.call(obj, prop);
比喻:__webpack_require__.o 是一个魔法检测器,用于判断对象是否包含指定的属性。它像一个魔法探测器,可以快速准确地告诉你某个魔法药剂是否含有特定成分。
意义:提供一个简短的方式检查对象是否包含某个属性,简化了代码逻辑,提高了代码的可读性和维护性。
6. 启动函数
最后,有一个启动函数用于加载入口模块并开始执行应用逻辑。这个函数会调用 __webpack_require__ 来加载入口模块,并将结果赋值给 __webpack_exports__ 。
比喻:最后,到了启动的时候了!Webpack 会从入口模块(./src/index.js)开始,像举行一场盛大的魔法仪式一样,逐步加载和执行所有的模块。最终,所有的魔法都汇聚在一起,形成了一个完整的魔法药剂(也就是你的应用程序)。这个过程就像是点燃了一个巨大的魔法火焰,照亮了整个城堡。
意义:加载入口模块并开始执行应用逻辑,确保整个应用能够正常启动和运行。它是整个打包文件的起点,触发了后续的所有操作。
7. 总结
- 整体立即执行
整个文件是一个立即执行函数。
- 两个主要变量:
__webpack_modules__:模块集合。
__webpack_module_cache__:模块缓存。
- 三个工具函数:
__webpack_require__.r:定义 **esModule 属性。
**webpack_require**.d:挂载导出的方法或变量。
**webpack_require__.o:检查对象属性。
- 一个启动函数
加载入口模块并开始执行应用逻辑。
即:整体立即执行,两个变量,三个工具函数,一个启动函数,简称 2+3+1 模式