🔧 构造函数初始化
-
初始化 3 个
WeakMap
存储结构:_modules
:Module → ChunkGraphModule_chunks
:Chunk → ChunkGraphChunk_blockChunkGroups
:AsyncDependenciesBlock → ChunkGroup
-
初始化
Map
:_runtimeIds
用于存储 runtime 对应的 ID。 -
保存
moduleGraph
引用。 -
指定哈希函数(默认
"md4"
)。 -
绑定
_getGraphRoots
方法。
📦 内部数据结构管理方法(私有)
-
_getChunkGraphModule(module)
:懒初始化并返回某个模块对应的ChunkGraphModule
。 -
_getChunkGraphChunk(chunk)
:懒初始化并返回某个 Chunk 对应的ChunkGraphChunk
。 -
_getGraphRoots(set)
:- 用于从模块集合中找出"图的根节点"模块(无其他模块依赖它的)。
- 内部会递归分析依赖关系并过滤掉 inactive 或 transitive-only 的连接。
🔗 连接 / 断开 Chunk 与 Module 的关系
-
connectChunkAndModule(chunk, module)
:- 将 Chunk 加入到模块的
chunks
集合中。 - 将 Module 加入到 Chunk 的
modules
集合中。
- 将 Chunk 加入到模块的
-
disconnectChunkAndModule(chunk, module)
:- 将 Module 从 Chunk 中移除。
- 同时也从 Module 中移除 Chunk。
- 清理相关的 sourceType 缓存。
-
disconnectChunk(chunk)
:- 将 Chunk 与其所有 Module 的连接断开。
- 清空模块列表。
- 断开与所有 ChunkGroup 的连接。
- 调用
ChunkGraph.clearChunkGraphForChunk
清理缓存。
📎 附加模块和运行时模块
-
attachModules(chunk, modules)
:- 向指定 Chunk 附加多个普通模块。
-
attachRuntimeModules(chunk, modules)
:- 向 Chunk 附加运行时代码模块(通常用于引导逻辑)。
-
attachFullHashModules(chunk, modules)
:- 添加影响 Chunk 全部 hash 值的模块。
-
attachDependentHashModules(chunk, modules)
:- 添加依赖其他模块 hash 值的模块(自身不影响)。
js
/**
* ChunkGraph 类
* 用于管理模块(Module)和 Chunk 之间的双向关系,
* 还会维护运行时代码、模块哈希依赖等构建信息。
*/
class ChunkGraph {
/**
* 构造函数
* @param {ModuleGraph} moduleGraph 模块依赖图(用于获取模块之间的连接)
* @param {string | Hash} hashFunction 使用的哈希函数(默认 "md4")
*/
constructor(moduleGraph, hashFunction = "md4") {
/**
* 模块映射表,用于关联每个 Module 到一个内部的 ChunkGraphModule 实例
* @private
* @type {WeakMap<Module, ChunkGraphModule>}
*/
this._modules = new WeakMap();
/**
* Chunk 映射表,用于关联每个 Chunk 到一个 ChunkGraphChunk 实例
* @private
* @type {WeakMap<Chunk, ChunkGraphChunk>}
*/
this._chunks = new WeakMap();
/**
* 异步依赖块(AsyncDependenciesBlock)映射到 ChunkGroup
* @private
* @type {WeakMap<AsyncDependenciesBlock, ChunkGroup>}
*/
this._blockChunkGroups = new WeakMap();
/**
* 运行时代码的 ID 映射(如 runtimeA -> id 0)
* @private
* @type {Map<string, string | number>}
*/
this._runtimeIds = new Map();
/** 模块图引用,用于查询模块间连接关系 */
this.moduleGraph = moduleGraph;
/** 哈希函数 */
this._hashFunction = hashFunction;
/** 绑定方法 */
this._getGraphRoots = this._getGraphRoots.bind(this);
}
/**
* 获取模块的内部管理对象(ChunkGraphModule)
* @private
* @param {Module} module 模块实例
* @returns {ChunkGraphModule}
*/
_getChunkGraphModule(module) {
let cgm = this._modules.get(module);
if (cgm === undefined) {
cgm = new ChunkGraphModule();
this._modules.set(module, cgm);
}
return cgm;
}
/**
* 获取 Chunk 的内部管理对象(ChunkGraphChunk)
* @private
* @param {Chunk} chunk Chunk 实例
* @returns {ChunkGraphChunk}
*/
_getChunkGraphChunk(chunk) {
let cgc = this._chunks.get(chunk);
if (cgc === undefined) {
cgc = new ChunkGraphChunk();
this._chunks.set(chunk, cgc);
}
return cgc;
}
/**
* 获取一个模块集合中的根模块(即没有其他模块依赖它)
* @param {SortableSet<Module>} set 模块集合
* @returns {Module[]} 根模块数组(按 identifier 排序)
*/
_getGraphRoots(set) {
const { moduleGraph } = this;
return Array.from(
findGraphRoots(set, module => {
/** @type {Set<Module>} */
const set = new Set();
/**
* 递归收集该模块的依赖模块
* @param {Module} module 当前模块
*/
const addDependencies = module => {
for (const connection of moduleGraph.getOutgoingConnections(module)) {
if (!connection.module) continue;
const activeState = connection.getActiveState(undefined);
if (activeState === false) continue;
if (activeState === ModuleGraphConnection.TRANSITIVE_ONLY) {
addDependencies(connection.module);
continue;
}
set.add(connection.module);
}
};
addDependencies(module);
return set;
})
).sort(compareModulesByIdentifier); // 保持模块顺序一致性
}
/**
* 连接一个 Chunk 和一个 Module
* @param {Chunk} chunk Chunk 实例
* @param {Module} module Module 实例
*/
connectChunkAndModule(chunk, module) {
const cgm = this._getChunkGraphModule(module);
const cgc = this._getChunkGraphChunk(chunk);
cgm.chunks.add(chunk); // 在模块记录中添加 chunk
cgc.modules.add(module); // 在 chunk 记录中添加模块
}
/**
* 断开 Chunk 与 Module 的连接
* @param {Chunk} chunk Chunk 实例
* @param {Module} module Module 实例
*/
disconnectChunkAndModule(chunk, module) {
const cgm = this._getChunkGraphModule(module);
const cgc = this._getChunkGraphChunk(chunk);
cgc.modules.delete(module); // 从 Chunk 中移除模块
if (cgc.sourceTypesByModule) cgc.sourceTypesByModule.delete(module); // 同步删除 sourceType 映射
cgm.chunks.delete(chunk); // 从 Module 中移除 Chunk
}
/**
* 断开一个 Chunk 与所有模块、组的关系
* @param {Chunk} chunk 要断开的 Chunk
*/
disconnectChunk(chunk) {
const cgc = this._getChunkGraphChunk(chunk);
// 从每个模块中移除对该 chunk 的引用
for (const module of cgc.modules) {
const cgm = this._getChunkGraphModule(module);
cgm.chunks.delete(chunk);
}
cgc.modules.clear(); // 清空 chunk 中的所有模块
chunk.disconnectFromGroups(); // 与 ChunkGroup 的连接也要断开
ChunkGraph.clearChunkGraphForChunk(chunk); // 清除 chunk 的相关缓存
}
/**
* 附加多个模块到某个 Chunk(建立模块-Chunk 的连接)
* @param {Chunk} chunk Chunk 实例
* @param {Iterable<Module>} modules 模块集合
*/
attachModules(chunk, modules) {
const cgc = this._getChunkGraphChunk(chunk);
for (const module of modules) {
cgc.modules.add(module);
}
}
/**
* 向 Chunk 附加运行时代码模块(例如 __webpack_require__ runtime)
* @param {Chunk} chunk Chunk 实例
* @param {Iterable<RuntimeModule>} modules 运行时代码模块
*/
attachRuntimeModules(chunk, modules) {
const cgc = this._getChunkGraphChunk(chunk);
for (const module of modules) {
cgc.runtimeModules.add(module);
}
}
/**
* 向 Chunk 附加那些会影响 Chunk 整体内容哈希的模块(如带 side effect 的 runtime 模块)
* @param {Chunk} chunk Chunk 实例
* @param {Iterable<RuntimeModule>} modules 模块集合
*/
attachFullHashModules(chunk, modules) {
const cgc = this._getChunkGraphChunk(chunk);
if (cgc.fullHashModules === undefined) cgc.fullHashModules = new Set();
for (const module of modules) {
cgc.fullHashModules.add(module);
}
}
/**
* 向 Chunk 附加那些依赖其他模块哈希的模块(但不影响自身)
* @param {Chunk} chunk Chunk 实例
* @param {Iterable<RuntimeModule>} modules 模块集合
*/
attachDependentHashModules(chunk, modules) {
const cgc = this._getChunkGraphChunk(chunk);
if (cgc.dependentHashModules === undefined)
cgc.dependentHashModules = new Set();
for (const module of modules) {
cgc.dependentHashModules.add(module);
}
}
}