ModuleGraph
是 Webpack 内部构建模块依赖图的核心管理器,负责追踪模块之间的引用、组织依赖关系、辅助生成优化后的构建结果。
🧱 核心数据结构
-
this._dependencyMap
- 类型:
WeakMap<Dependency, ModuleGraphConnection | null>
- 用途:存储无来源模块的依赖与连接关系
- 类型:
-
this._moduleMap
- 类型:
Map<Module, ModuleGraphModule>
- 用途:记录模块对应的图节点信息
- 类型:
-
this._metaMap
- 类型:
WeakMap<any, object>
- 用途:用于记录任意对象的元信息(缓存用途)
- 类型:
-
this._cache
- 类型:
WeakTupleMap<any[], any> | undefined
- 用途:内部使用的缓存机制
- 类型:
-
this._moduleMemCaches
- 类型:
Map<Module, WeakTupleMap<any, any>> | undefined
- 用途:模块级别的缓存存储
- 类型:
-
this._cacheStage
- 类型:
string | undefined
- 用途:用于标识当前缓存的构建阶段
- 类型:
⚙️ 主要方法功能分类
-
依赖与模块关系管理
setResolvedModule
:设置依赖指向的模块连接updateModule
:更新依赖对应的模块removeConnection
:移除依赖与模块的连接addExplanation
:为依赖连接添加说明信息
-
模块图节点处理
_getModuleGraphModule
:获取或新建模块图节点cloneModuleAttributes
:复制模块的遍历信息(用于图遍历)removeModuleAttributes
:清空单个模块的图信息removeAllModuleAttributes
:清空所有模块图信息(如重构阶段)
-
依赖父级管理
setParents
:设置依赖的父模块、父块和索引getParentModule
:获取依赖的父模块getParentBlock
:获取依赖的父块getParentBlockIndex
:获取依赖在块中的位置
-
模块连接迁移
moveModuleConnections
:将符合条件的连接从一个模块迁移到另一个模块(比如合并模块时使用)
js
constructor() {
// 用于存储依赖到连接(ModuleGraphConnection)的映射,使用 WeakMap 防止内存泄漏
this._dependencyMap = new WeakMap();
// 存储模块(Module)到 ModuleGraphModule 的映射
this._moduleMap = new Map();
// 存储任意对象到元数据对象的映射,用于缓存额外信息,使用 WeakMap 避免泄漏
this._metaMap = new WeakMap();
// 一个弱元组键缓存,用于性能优化(可选)
this._cache = undefined;
// 每个模块对应一个弱元组缓存,用于模块级的缓存加速(可选)
this._moduleMemCaches = undefined;
// 缓存阶段标志(例如构建过程中的某个阶段标识)
this._cacheStage = undefined;
}
/**
* 获取一个模块对应的 ModuleGraphModule 对象,若不存在则创建并保存
* @param {Module} module 模块对象
* @returns {ModuleGraphModule}
*/
_getModuleGraphModule(module) {
let mgm = this._moduleMap.get(module);
if (mgm === undefined) {
mgm = new ModuleGraphModule();
this._moduleMap.set(module, mgm);
}
return mgm;
}
/**
* 设置某个依赖的"父模块"、"父依赖块"以及在块中的索引
* @param {Dependency} dependency 当前依赖对象
* @param {DependenciesBlock} block 父依赖块
* @param {Module} module 所属的模块
* @param {number=} indexInBlock 在依赖块中的位置(默认 -1)
*/
setParents(dependency, block, module, indexInBlock = -1) {
dependency._parentDependenciesBlockIndex = indexInBlock;
dependency._parentDependenciesBlock = block;
dependency._parentModule = module;
}
/**
* 获取某依赖所属的父模块
* @param {Dependency} dependency 当前依赖
* @returns {Module | undefined}
*/
getParentModule(dependency) {
return dependency._parentModule;
}
/**
* 获取某依赖所属的父依赖块
* @param {Dependency} dependency 当前依赖
* @returns {DependenciesBlock | undefined}
*/
getParentBlock(dependency) {
return dependency._parentDependenciesBlock;
}
/**
* 获取依赖在其父依赖块中的位置索引
* @param {Dependency} dependency 当前依赖
* @returns {number}
*/
getParentBlockIndex(dependency) {
return dependency._parentDependenciesBlockIndex;
}
/**
* 为某个依赖设置其所解析到的模块,并在模块图中建立连接关系
* @param {Module | null} originModule 源模块(发起引用)
* @param {Dependency} dependency 当前依赖
* @param {Module} module 目标模块(被引用)
*/
setResolvedModule(originModule, dependency, module) {
const connection = new ModuleGraphConnection(
originModule,
dependency,
module,
undefined,
dependency.weak,
dependency.getCondition(this)
);
// 将连接添加到目标模块的"传入连接"中
const connections = this._getModuleGraphModule(module).incomingConnections;
connections.add(connection);
if (originModule) {
const mgm = this._getModuleGraphModule(originModule);
if (mgm._unassignedConnections === undefined) {
mgm._unassignedConnections = [];
}
mgm._unassignedConnections.push(connection);
// 若源模块还未有 outgoingConnections,初始化它
if (mgm.outgoingConnections === undefined) {
mgm.outgoingConnections = new SortableSet();
}
mgm.outgoingConnections.add(connection);
} else {
// 若没有 originModule,就直接记录在 dependencyMap 中
this._dependencyMap.set(dependency, connection);
}
}
/**
* 更新依赖的目标模块,并将连接替换为新模块
* @param {Dependency} dependency 当前依赖
* @param {Module} module 新的目标模块
*/
updateModule(dependency, module) {
const connection = /** @type {ModuleGraphConnection} */ (this.getConnection(dependency));
if (connection.module === module) return;
const newConnection = connection.clone();
newConnection.module = module;
this._dependencyMap.set(dependency, newConnection);
connection.setActive(false);
const originMgm = this._getModuleGraphModule(/** @type {Module} */ (connection.originModule));
originMgm.outgoingConnections.add(newConnection);
const targetMgm = this._getModuleGraphModule(module);
targetMgm.incomingConnections.add(newConnection);
}
/**
* 移除某个依赖的连接关系
* @param {Dependency} dependency 当前依赖
*/
removeConnection(dependency) {
const connection = /** @type {ModuleGraphConnection} */ (this.getConnection(dependency));
const targetMgm = this._getModuleGraphModule(connection.module);
targetMgm.incomingConnections.delete(connection);
const originMgm = this._getModuleGraphModule(/** @type {Module} */ (connection.originModule));
originMgm.outgoingConnections.delete(connection);
this._dependencyMap.set(dependency, null);
}
/**
* 给某个依赖的连接添加说明文字(一般用于调试或解释)
* @param {Dependency} dependency 当前依赖
* @param {string} explanation 描述信息
*/
addExplanation(dependency, explanation) {
const connection = /** @type {ModuleGraphConnection} */ (this.getConnection(dependency));
connection.addExplanation(explanation);
}
/**
* 克隆源模块上的图属性(例如深度、索引等)到目标模块上
* @param {Module} sourceModule 源模块
* @param {Module} targetModule 目标模块
*/
cloneModuleAttributes(sourceModule, targetModule) {
const oldMgm = this._getModuleGraphModule(sourceModule);
const newMgm = this._getModuleGraphModule(targetModule);
newMgm.postOrderIndex = oldMgm.postOrderIndex;
newMgm.preOrderIndex = oldMgm.preOrderIndex;
newMgm.depth = oldMgm.depth;
newMgm.exports = oldMgm.exports;
newMgm.async = oldMgm.async;
}
/**
* 移除某个模块上的遍历属性和 async 标志
* @param {Module} module 要清理的模块
*/
removeModuleAttributes(module) {
const mgm = this._getModuleGraphModule(module);
mgm.postOrderIndex = null;
mgm.preOrderIndex = null;
mgm.depth = null;
mgm.async = false;
}
/**
* 移除所有模块上的遍历属性(清空状态)
*/
removeAllModuleAttributes() {
for (const mgm of this._moduleMap.values()) {
mgm.postOrderIndex = null;
mgm.preOrderIndex = null;
mgm.depth = null;
mgm.async = false;
}
}
/**
* 将某个模块上的连接迁移到另一个模块(满足过滤条件的连接)
* @param {Module} oldModule 原始模块
* @param {Module} newModule 新模块
* @param {function(ModuleGraphConnection): boolean} filterConnection 判断哪些连接需要迁移
*/
moveModuleConnections(oldModule, newModule, filterConnection) {
if (oldModule === newModule) return;
const oldMgm = this._getModuleGraphModule(oldModule);
const newMgm = this._getModuleGraphModule(newModule);
// 处理 outgoing connections(从 oldModule 出发)
const oldConnections = oldMgm.outgoingConnections;
if (oldConnections !== undefined) {
if (newMgm.outgoingConnections === undefined) {
newMgm.outgoingConnections = new SortableSet();
}
const newConnections = newMgm.outgoingConnections;
for (const connection of oldConnections) {
if (filterConnection(connection)) {
connection.originModule = newModule;
newConnections.add(connection);
oldConnections.delete(connection);
}
}
}
// 处理 incoming connections(指向 oldModule)
const oldConnections2 = oldMgm.incomingConnections;
const newConnections2 = newMgm.incomingConnections;
for (const connection of oldConnections2) {
if (filterConnection(connection)) {
connection.module = newModule;
newConnections2.add(connection);
oldConnections2.delete(connection);
}
}
}