!(function(moduleDefinitions) {
// 用于存储已加载的模块
const loadedModules = {};
// Webpack 核心加载函数,用于动态加载模块
function __webpack_require__(moduleId) {
// 1. 如果模块已缓存,直接返回其导出对象
if (loadedModules[moduleId]) {
return loadedModules[moduleId].exports;
}
// 2. 创建新模块对象并缓存
const module = loadedModules[moduleId] = {
id: moduleId, // 模块 ID
isLoaded: false, // 是否已加载标志
exports: {} // 模块的导出对象
};
// 3. 执行模块函数,传入 module、exports 和 __webpack_require__
moduleDefinitions[moduleId].call(module.exports, module, module.exports, __webpack_require__);
// 4. 标记模块为已加载并返回导出对象
module.isLoaded = true;
return module.exports;
}
// 绑定属性到 __webpack_require__
__webpack_require__.m = moduleDefinitions; // 所有模块的定义
__webpack_require__.c = loadedModules; // 已缓存的模块
// 定义模块导出属性的 getter 函数
__webpack_require__.d = function(targetExports, propertyName, propertyGetter) {
if (!__webpack_require__.o(targetExports, propertyName)) {
Object.defineProperty(targetExports, propertyName, {
enumerable: true, // 可枚举
get: propertyGetter // getter 函数动态获取值
});
}
};
// 标记对象为 ES 模块
__webpack_require__.r = function(targetExports) {
if (typeof Symbol !== 'undefined' && Symbol.toStringTag) {
Object.defineProperty(targetExports, Symbol.toStringTag, { value: 'Module' });
}
Object.defineProperty(targetExports, '__esModule', { value: true });
};
// 处理不同类型的模块导入(兼容 CommonJS 和 ES Module)
__webpack_require__.t = function(importValue, importMode) {
if (importMode & 1) importValue = __webpack_require__(importValue); // 加载模块
if (importMode & 8) return importValue; // 直接返回
if (importMode & 4 && typeof importValue === 'object' && importValue && importValue.__esModule) {
return importValue; // 返回 ES 模块
}
const namespace = Object.create(null); // 创建新命名空间
__webpack_require__.r(namespace); // 标记为 ES 模块
Object.defineProperty(namespace, 'default', {
enumerable: true,
value: importValue
});
if (importMode & 2 && typeof importValue !== 'string') {
for (const key in importValue) {
__webpack_require__.d(namespace, key, (key => importValue[key]).bind(null, key));
}
}
return namespace;
};
// 获取模块的默认导出
__webpack_require__.n = function(sourceModule) {
const defaultGetter = sourceModule && sourceModule.__esModule
? function() { return sourceModule['default']; } // ES 模块默认导出
: function() { return sourceModule; }; // CommonJS 模块
__webpack_require__.d(defaultGetter, 'a', defaultGetter);
return defaultGetter;
};
// 判断对象是否拥有某个属性
__webpack_require__.o = function(targetObject, propertyName) {
return Object.prototype.hasOwnProperty.call(targetObject, propertyName);
};
// Webpack 的公共路径(通常用于动态加载资源)
__webpack_require__.p = "";
// 指定入口模块 ID 并加载
__webpack_require__.s = 3;
return __webpack_require__(__webpack_require__.s);
})([
// 模块 0:日志输出组件
function(module, exports) {
function logMessage() {
console.log("日志组件被调用");
}
module.exports = logMessage;
},
// 模块 1:数据返回组件
function(module, exports) {
function getData() {
return {
text: "数据组件返回的信息"
};
}
module.exports = getData;
},
// 模块 2:组合其他组件
function(module, exports, __webpack_require__) {
const logMessage = __webpack_require__(0); // 引用模块 0
const getData = __webpack_require__(1); // 引用模块 1
function mainComponent() {
logMessage();
console.log(getData().text);
}
module.exports = mainComponent;
},
// 模块 3:入口模块,导出组件集合
function(module, exports, __webpack_require__) {
"use strict";
__webpack_require__.r(exports); // 标记 exports 为 ES 模块
const componentExports = {};
__webpack_require__.r(componentExports); // 标记 componentExports 为 ES 模块
// 定义导出的组件
__webpack_require__.d(componentExports, "TiAccordion", function() {
return accordionModule.a;
});
__webpack_require__.d(componentExports, "TiActionMenu", function() {
return actionMenuModule.a;
});
// 加载依赖模块
const logModule = __webpack_require__(0);
const accordionModule = __webpack_require__.n(logModule);
const dataModule = __webpack_require__(1);
const actionMenuModule = __webpack_require__.n(dataModule);
// Polyfill Object.values(兼容旧环境)
Object.values = Object.values || function(targetObject) {
if (targetObject !== Object(targetObject)) {
throw new TypeError("Object.values called on a non-object");
}
const values = [];
for (const key in targetObject) {
if (Object.prototype.hasOwnProperty.call(targetObject, key)) {
values.push(targetObject[key]);
}
}
return values;
};
// 输出组件集合
document.body.innerHTML = Object.values(componentExports).join(', ');
console.log("....", Object.values(componentExports));
}
]);
这一个简化版的 Webpack 运行时模块加载器,它模拟 Webpack 如何管理和执行模块。以下是详细的解析:
1. 顶层立即执行函数 (IIFE)
代码的最外层是一个 立即执行函数表达式 (IIFE),它接受 moduleDefinitions
作为参数,这是一个 模块定义数组,其中每个索引对应一个模块的实现:
!(function(moduleDefinitions) {
// Webpack 加载器逻辑
})([ /* 模块定义数组 */ ]);
其中:
moduleDefinitions
是一个数组,每个元素都是一个模块的 定义函数。
2. Webpack 的核心加载函数 (__webpack_require__
)
Webpack 运行时的核心是 __webpack_require__
,它负责:
- 检查模块是否已被缓存
- 如果未缓存,则创建一个新的模块对象
- 执行模块代码
- 标记模块已加载
- 返回模块的导出对象
代码:
function __webpack_require__(moduleId) {
if (loadedModules[moduleId]) {
return loadedModules[moduleId].exports;
}
const module = loadedModules[moduleId] = {
id: moduleId,
isLoaded: false,
exports: {}
};
moduleDefinitions[moduleId].call(module.exports, module, module.exports, __webpack_require__);
module.isLoaded = true;
return module.exports;
}
loadedModules
作为缓存,避免重复加载。moduleDefinitions[moduleId]
通过.call()
方式执行,传入module, exports, __webpack_require__
。
3. Webpack 运行时的工具函数
Webpack 还提供了一些工具函数来处理 ES6 import/export
兼容性问题:
(1)__webpack_require__.d
定义 ES6 的 getter
形式的模块导出:
__webpack_require__.d = function(targetExports, propertyName, propertyGetter) {
if (!__webpack_require__.o(targetExports, propertyName)) {
Object.defineProperty(targetExports, propertyName, {
enumerable: true,
get: propertyGetter
});
}
};
- 作用:定义 动态计算 的导出属性,支持
import { name } from 'module'
。
(2)__webpack_require__.r
标记某个模块为 ES Module:
__webpack_require__.r = function(targetExports) {
if (typeof Symbol !== 'undefined' && Symbol.toStringTag) {
Object.defineProperty(targetExports, Symbol.toStringTag, { value: 'Module' });
}
Object.defineProperty(targetExports, '__esModule', { value: true });
};
- 作用:让
targetExports.__esModule = true
,这样 Webpack 知道它是一个 ES6 模块。
(3)__webpack_require__.t
支持 import * as
形式:
__webpack_require__.t = function(importValue, importMode) {
if (importMode & 1) importValue = __webpack_require__(importValue);
if (importMode & 8) return importValue;
if (importMode & 4 && typeof importValue === 'object' && importValue && importValue.__esModule) {
return importValue;
}
const namespace = Object.create(null);
__webpack_require__.r(namespace);
Object.defineProperty(namespace, 'default', { enumerable: true, value: importValue });
if (importMode & 2 && typeof importValue !== 'string') {
for (const key in importValue) {
__webpack_require__.d(namespace, key, (key => importValue[key]).bind(null, key));
}
}
return namespace;
};
importMode
的值:1
:加载模块8
:直接返回4
:如果是 ES6 模块,则直接返回2
:把模块的所有属性导出(用于import *
)
(4)__webpack_require__.n
处理 CommonJS 默认导出 的兼容性:
__webpack_require__.n = function(sourceModule) {
const defaultGetter = sourceModule && sourceModule.__esModule
? function() { return sourceModule['default']; }
: function() { return sourceModule; };
__webpack_require__.d(defaultGetter, 'a', defaultGetter);
return defaultGetter;
};
- 作用:确保 CommonJS 和 ES6 的
default
导出 兼容。
(5)__webpack_require__.o
用于检查 对象是否包含某个属性:
__webpack_require__.o = function(targetObject, propertyName) {
return Object.prototype.hasOwnProperty.call(targetObject, propertyName);
};
4. 入口模块的执行
__webpack_require__.s = 3;
return __webpack_require__(__webpack_require__.s);
__webpack_require__.s = 3
表示 入口模块 是moduleDefinitions[3]
。- 然后调用
__webpack_require__(3)
运行主模块。
5. 各个模块的定义
Webpack 运行时会将 源代码的不同部分拆分成多个模块,每个模块在 moduleDefinitions
数组中有对应的索引。
// 模块 0:日志组件
function(module, exports) {
function logMessage() {
console.log("日志组件被调用");
}
module.exports = logMessage;
}
- 作用:定义
logMessage
函数,并通过module.exports
导出。
// 模块 1:数据返回组件
function(module, exports) {
function getData() {
return {
text: "数据组件返回的信息"
};
}
module.exports = getData;
}
- 作用:定义
getData
函数,返回一个对象。
// 模块 2:组合其他组件
function(module, exports, __webpack_require__) {
const logMessage = __webpack_require__(0);
const getData = __webpack_require__(1);
function mainComponent() {
logMessage();
console.log(getData().text);
}
module.exports = mainComponent;
}
- 作用:加载
logMessage
和getData
,并组合使用。
// 模块 3:入口模块
function(module, exports, __webpack_require__) {
"use strict";
__webpack_require__.r(exports);
const componentExports = {};
__webpack_require__.r(componentExports);
__webpack_require__.d(componentExports, "TiAccordion", function() {
return accordionModule.a;
});
__webpack_require__.d(componentExports, "TiActionMenu", function() {
return actionMenuModule.a;
});
const logModule = __webpack_require__(0);
const accordionModule = __webpack_require__.n(logModule);
const dataModule = __webpack_require__(1);
const actionMenuModule = __webpack_require__.n(dataModule);
// 兼容 `Object.values`
Object.values = Object.values || function(targetObject) {
if (targetObject !== Object(targetObject)) {
throw new TypeError("Object.values called on a non-object");
}
const values = [];
for (const key in targetObject) {
if (Object.prototype.hasOwnProperty.call(targetObject, key)) {
values.push(targetObject[key]);
}
}
return values;
};
console.log("....", Object.values(componentExports));
}
- 作用:
- 定义
componentExports
- 兼容
Object.values
- 运行 Webpack 入口逻辑
- 定义
总结
Webpack 运行时的作用
- 缓存模块
- 动态加载模块
- 处理 ES6 和 CommonJS 兼容性
- 定义模块的
import/export
模块的执行
moduleDefinitions[3]
作为入口点- 依赖模块
0,1,2
先被加载
代码结构
- Webpack 模拟了 Node.js 的
require
方式,同时兼容 ES6import/export
。
- Webpack 模拟了 Node.js 的