本文章对webpack5的模块化进行详细介绍
源代码
// a.js
export default class Math {
constructor() {
this.name = "Math";
}
add(a, b) {
return a + b;
}
}
// b.js
export default class User {
constructor(name, age) {
this.name = name;
this.age = age;
}
say() {
console.log(`I am ${this.name}, ${this.age} years old`);
}
}
// index.js
import Math from './a'
import User from './b'
const math = new Math()
const user = new User('zhangSan', 18)
user.say()
const { add } = math;
console.log('sss', add(1, 2));
user.say();
// webpack.config.js
const path = require('path');
module.exports = {
entry: './src/index.js',
output: {
filename: 'bundle.js',
path: path.resolve(__dirname, 'dist'),
},
devServer: {
static: {
directory: path.join(__dirname, 'dist'),
},
compress: true,
port: 9000,
},
};
bundle后的代码
(() => {
"use strict";
// 定义模块集合, 包含Math和User两个模块
var __webpack_modules__ = ([
,
((__unused_webpack_module, __webpack_exports__, __webpack_require__) => {
__webpack_require__.r(__webpack_exports__);
__webpack_require__.d(__webpack_exports__, {
"default": () => (Math)
});
class Math {
constructor() {
this.name = "Math";
}
add(a, b) {
return a + b;
}
}
}),
((__unused_webpack_module, __webpack_exports__, __webpack_require__) => {
__webpack_require__.r(__webpack_exports__);
__webpack_require__.d(__webpack_exports__, {
"default": () => (User)
});
class User {
constructor(name, age) {
this.name = name;
this.age = age;
}
say() {
console.log(`I am ${this.name}, ${this.age} years old`);
}
}
})
]);
// 模块缓存对象
var __webpack_module_cache__ = {};
/**
* webpack的模块加载函数
* @param {number} moduleId - 模块ID
* @returns {object} 模块的导出内容
*/
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;
}
(() => {
/**
* 为exports对象定义getter属性
* @param {object} exports - 导出对象
* @param {object} definition - 属性定义
*/
__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] });
}
}
};
})();
(() => {
/**
* 检查对象是否包含指定属性
* @param {object} obj - 要检查的对象
* @param {string} prop - 属性名
* @returns {boolean} 是否包含该属性
*/
__webpack_require__.o = (obj, prop) => (Object.prototype.hasOwnProperty.call(obj, prop))
})();
(() => {
/**
* 将exports标记为ES模块
* @param {object} exports - 要标记的导出对象
*/
__webpack_require__.r = (exports) => {
if(typeof Symbol !== 'undefined' && Symbol.toStringTag) {
Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
}
Object.defineProperty(exports, '__esModule', { value: true });
};
})();
// 入口代码
var __webpack_exports__ = {};
__webpack_require__.r(__webpack_exports__);
var _a__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(1);
var _b__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(2);
// 创建实例并调用方法
const math = new _a__WEBPACK_IMPORTED_MODULE_0__["default"]()
const user = new _b__WEBPACK_IMPORTED_MODULE_1__["default"]('zhangSan', 18)
user.say()
const { add } = math;
console.log('sss', add(1, 2));
user.say();
})();
Webpack5 模块化原理详解
1. 整体架构
Webpack5的模块系统主要由以下几个核心部分组成:
// 1. 模块定义数组
var __webpack_modules__ = ([...]);
// 2. 模块缓存
var __webpack_module_cache__ = {};
// 3. 模块加载函数
function __webpack_require__(moduleId) {...}
// 4. 运行时工具函数
__webpack_require__.d = (exports, definition) => {...} // 定义属性
__webpack_require__.o = (obj, prop) => {...} // 属性检查
__webpack_require__.r = (exports) => {...} // ES模块标记
2. 模块定义结构
2.1 模块数组
每个模块被转换为一个函数,存储在__webpack_modules__
数组中:
var __webpack_modules__ = ([
, // 索引0处空置
((module, exports, require) => {
// 模块1: Math类
}),
((module, exports, require) => {
// 模块2: User类
})
]);
2.2 模块函数参数
每个模块函数接收三个关键参数:
module
: 当前模块对象exports
: 模块导出对象require
: 模块加载函数
3. 模块加载机制
3.1 模块缓存
var __webpack_module_cache__ = {};
- 用于存储已加载的模块
- 避免重复加载同一模块
- 实现模块单例模式
3.2 模块加载函数
function __webpack_require__(moduleId) {
// 1. 检查缓存
var cachedModule = __webpack_module_cache__[moduleId];
if (cachedModule !== undefined) {
return cachedModule.exports;
}
// 2. 创建新模块
var module = __webpack_module_cache__[moduleId] = {
exports: {}
};
// 3. 执行模块函数
__webpack_modules__[moduleId](module, module.exports, __webpack_require__);
// 4. 返回模块导出
return module.exports;
}
4. ES模块支持
4.1 ES模块标记
__webpack_require__.r = (exports) => {
// 添加 __esModule 标记
Object.defineProperty(exports, '__esModule', { value: true });
// 添加 Symbol.toStringTag 标记
if(typeof Symbol !== 'undefined' && Symbol.toStringTag) {
Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
}
};
4.2 属性定义
__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]
});
}
}
};
5. 实际运用示例
以示例代码中的Math类模块为例:
// 1. 导入模块
var _a__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(1);
// 2. 创建实例
const math = new _a__WEBPACK_IMPORTED_MODULE_0__["default"]()
// 3. 使用模块方法
const { add } = math;
console.log('sss', add(1, 2));
6. 模块加载流程
- 入口文件通过 IIFE (立即执行函数) 启动
- 调用
__webpack_require__
加载首个模块 - 检查模块缓存
- 如果未缓存,创建新模块实例
- 执行模块函数,填充 exports 对象
- 缓存并返回模块导出
- 在模块间建立依赖关系
webpack4 代码参考
// modules是存放所有模块的数组,数组中每个元素存储{ 模块路径: 模块导出代码函数 }
(function(modules) {
// 模块缓存作用,已加载的模块可以不用再重新读取,提升性能
var installedModules = {};
// 关键函数,加载模块代码
// 形式有点像Node的CommonJS模块,但这里是可跑在浏览器上的es5代码
function __webpack_require__(moduleId) {
// 缓存检查,有则直接从缓存中取得
if(installedModules[moduleId]) {
return installedModules[moduleId].exports;
}
// 先创建一个空模块,塞入缓存中
var module = installedModules[moduleId] = {
i: moduleId,
l: false, // 标记是否已经加载
exports: {} // 初始模块为空
};
// 把要加载的模块内容,挂载到module.exports上
modules[moduleId].call(module.exports, module, module.exports, __webpack_require__);
module.l = true; // 标记为已加载
// 返回加载的模块,调用方直接调用即可
return module.exports;
}
// __webpack_require__对象下的r函数
// 在module.exports上定义__esModule为true,表明是一个模块对象
__webpack_require__.r = function(exports) {
Object.defineProperty(exports, '__esModule', { value: true });
};
// 启动入口模块main.js
return __webpack_require__(__webpack_require__.s = "./src/main.js");
})
({
// add模块
"./src/add.js": (function(module, __webpack_exports__, __webpack_require__) {
// 在module.exports上定义__esModule为true
__webpack_require__.r(__webpack_exports__);
// 直接把add模块内容,赋给module.exports.default对象上
__webpack_exports__["default"] = (function(a, b) {
let { name } = { name: 'hello world,'}
return name + a + b
});
}),
// 入口模块
"./src/main.js": (function(module, __webpack_exports__, __webpack_require__) {
__webpack_require__.r(__webpack_exports__)
// 拿到add模块的定义
// _add__WEBPACK_IMPORTED_MODULE_0__ = module.exports,有点类似require
var _add__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__("./src/add.js");
// add模块内容: _add__WEBPACK_IMPORTED_MODULE_0__["default"]
console.log(_add__WEBPACK_IMPORTED_MODULE_0__["default"], Object(_add__WEBPACK_IMPORTED_MODULE_0__["default"])(1, 2))
})
});