webpack 模块化
webpack 输出产物模块结构
假设有下面两个模块文件
// 1. src/moduleA.js
export const greeting = 'Hello';
// 2. src/moduleB.js
import { greeting } from './moduleA';
console.log(`${greeting}, World!`);
$ npx webpack ./webpack.moduleB.js -o ./bundle.js --mode=development
打包产物文件 bundle.js
// dist/bundle.js
/*
* ATTENTION: The "eval" devtool has been used (maybe by default in mode: "development").
* This devtool is neither made for production nor for readable output files.
* It uses "eval()" calls to create a separate source file in the browser devtools.
* If you are trying to read the output file, select a different devtool (https://webpack.js.org/configuration/devtool/)
* or disable the default devtool with "devtool: false".
* If you are looking for production-ready output files, see mode: "production" (https://webpack.js.org/configuration/mode/).
*/
/******/ (() => { // webpackBootstrap
/******/ "use strict";
/******/ var __webpack_modules__ = ({
/***/ "./webpack.moduleA.js":
/*!****************************!*\
!*** ./webpack.moduleA.js ***!
\****************************/
/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => {
eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export */ __webpack_require__.d(__webpack_exports__, {\n/* harmony export */ greeting: () => (/* binding */ greeting)\n/* harmony export */ });\nconst greeting = 'Hello';\n\n//# sourceURL=webpack:///./webpack.moduleA.js?");
/***/ }),
/***/ "./webpack.moduleB.js":
/*!****************************!*\
!*** ./webpack.moduleB.js ***!
\****************************/
/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => {
eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _webpack_moduleA_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./webpack.moduleA.js */ \"./webpack.moduleA.js\");\n\nconsole.log(`${_webpack_moduleA_js__WEBPACK_IMPORTED_MODULE_0__.greeting}, World!`);\n\n//# sourceURL=webpack:///./webpack.moduleB.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;
/******/ }
/******/
/************************************************************************/
/******/ /* 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__("./webpack.moduleB.js");
/******/
/******/ })()
;
代码分析:
- WebpackBootstrap
- 打包输出的代码以一个立即执行函数(Immediately Invoked Function Expression,IIFE)的形式开始,命名为
webpackBootstrap
。这个函数负责初始化 Webpack 运行环境并加载模块。
- webpack_modules 和 webpack_module_cache
__webpack_modules__
对象存储了所有模块的定义和加载逻辑,每个模块的路径作为键,对应的加载函数作为值
- webpack_require 函数
__webpack_require__
函数是模块加载器的核心,用于加载模块并执行模块的代码。它接受一个模块路径作为参数,并返回该模块的导出对象。
- 模块定义
- 每个模块的定义被封装在一个匿名函数中,并以模块路径为键存储在
__webpack_modules__
对象中。这些匿名函数接受三个参数:module
、exports
和__webpack_require__
- 在这些匿名函数中,模块的代码被执行,并且模块的导出对象(exports)被赋值为模块的导出内容。
- 模块加载和执行
- 每个模块的加载逻辑都由
__webpack_require__
函数完成。当调用__webpack_require__(moduleId)
时,它会根据模块路径从__webpack_modules__
对象中获取模块的定义,并执行相应的加载函数,然后返回模块的导出对象。
- 模块导出
- 模块的导出内容被定义在模块的
exports
对象中,可以通过__webpack_require__
函数加载并访问。
- 模块之间的依赖关系
- 模块之间的依赖关系通过
__webpack_require__
函数动态加载和解析,每个模块在加载时都会递归地加载它所依赖的模块,并确保模块的依赖关系被满足。
webpack dynamic import
Webpack 中的动态 import 实际上是通过 ECMAScript 的提案中的动态 import 实现的。动态 import 允许在代码运行时异步地加载模块,而不是在编译时静态地加载模块。
- **解析代码:**当 Webpack 解析代码时,会识别并解析所有的 import 语句,包括静态 import 和动态 import。
- **生成 Chunk:**当遇到动态 import 语句时,Webpack 会将该 import 语句所在的模块单独打包成一个 Chunk(代码块)。
- **代码分割:**Webpack 会根据动态 import 语句的位置和依赖关系,将相应的模块分割成不同的 Chunk。每个 Chunk 都会包含一个独立的模块或模块集合。
- **异步加载:**在运行时,当遇到动态 import 语句时,浏览器会异步地加载相应的 Chunk。Webpack 会生成一个新的 HTTP 请求,请求对应的 Chunk 文件,并在文件加载完成后执行相应的代码。
- **模块执行:**一旦 Chunk 文件加载完成,其中的模块就会被执行。这样,动态 import 的模块就可以在运行时被动态加载和执行了。
const constructor = {
'index.js': {
entry: 'index.js',
sourceCode: `
console.log('1')
`,
dependenceGraph: [
'./a.js',
'./b.js'
],
},
'./a.js': {
},
'./b.js': {
}
}
(function() {
const module = {
'index.js': {
sourceCode: ``
},
'./a.js': {
// ...
},
'./b.js': {
// ...
}
}
function requireFn(entry) {
const sourceMap = {}
}
requireFn('./index.js')
})()