这段代码是 Webpack 构建阶段“模块与输出代码结构之间关系图”的核心实现之一,提供了模块替换、关系查询、chunk 构成等核心功能支持。
🌐 核心概念
Module: 表示一个模块(通常是一个 JS 文件)。Chunk: 表示一组打包后的模块代码块(可能异步加载)。ChunkGraphModule (cgm): 模块在图中的元信息(如所属 chunk、是否是入口模块等)。ChunkGraphChunk (cgc): chunk 在图中的元信息(如包含的模块、运行时模块等)。RuntimeModule: 专用于 runtime 的模块(如 webpack_require 的实现)。Entrypoint: webpack 的入口定义,表示某个模块是 chunk 的起始点。
🧠 核心方法功能总结
✅ 1. 模块替换逻辑
-
replaceModule(oldModule, newModule)-
将
oldModule在所有 chunk 中替换为newModule,确保 graph 中所有引用和结构正确更新。 -
涉及:
- 普通模块 (
modules) - 入口模块 (
entryModules) - 运行时模块 (
runtimeModules,fullHashModules,dependentHashModules)
- 普通模块 (
-
🔍 2. 查询模块是否在某处
isModuleInChunk(module, chunk):模块是否在指定 chunk 中isModuleInChunkGroup(module, chunkGroup):模块是否在某个 chunkGroup(多个 chunk)中isEntryModule(module):模块是否是某个 chunk 的入口模块
🔄 3. 获取模块关联的 chunks
getModuleChunksIterable(module):获取某模块对应的 chunk 集合(可迭代)getOrderedModuleChunksIterable(module, sortFn):按指定排序方式返回 chunk 集合getModuleChunks(module):获取模块的 chunk(缓存版本)getNumberOfModuleChunks(module):获取模块关联 chunk 的数量
⚙️ 4. 获取模块的运行时环境
getModuleRuntimes(module):返回模块对应的运行时环境集合
📦 5. 获取 chunk 内模块的信息
getNumberOfChunkModules(chunk):chunk 中模块的总数getNumberOfChunkFullHashModules(chunk):chunk 中用于 full hash 的模块数量getChunkModulesIterable(chunk):chunk 中的模块集合getChunkModulesIterableBySourceType(chunk, sourceType):按sourceType获取 chunk 中的模块集合
🧩 设计亮点
- 使用了
_getChunkGraphModule和_getChunkGraphChunk来统一获取抽象结构,封装性强。 - 模块和 chunk 的关系被抽象成图状结构,极易扩展、分析依赖。
- 全面缓存(
getFromCache,getFromUnorderedCache)优化性能,适合大项目构建场景。 - 支持运行时代码模块(
RuntimeModule)的替换与追踪,对 HMR 和 runtime 拆分支持好。
/**
* @param {Module} oldModule the replaced module
* @param {Module} newModule the replacing module
* @returns {void}
*/
replaceModule(oldModule, newModule) { // 将旧模块 oldModule 替换为新模块 newModule
const oldCgm = this._getChunkGraphModule(oldModule); // 获取旧模块对应的 ChunkGraphModule 对象
const newCgm = this._getChunkGraphModule(newModule); // 获取新模块对应的 ChunkGraphModule 对象
for (const chunk of oldCgm.chunks) { // 遍历旧模块所在的所有 chunk
const cgc = this._getChunkGraphChunk(chunk); // 获取 chunk 对应的 ChunkGraphChunk 对象
cgc.modules.delete(oldModule); // 从 chunk 的模块集合中移除旧模块
cgc.modules.add(newModule); // 将新模块添加到该 chunk 的模块集合中
newCgm.chunks.add(chunk); // 将 chunk 添加到新模块的 chunk 集合中
}
oldCgm.chunks.clear(); // 清空旧模块的 chunk 集合
if (oldCgm.entryInChunks !== undefined) { // 如果旧模块在某些 chunk 中是入口模块
if (newCgm.entryInChunks === undefined) { // 如果新模块还没有 entryInChunks 属性
newCgm.entryInChunks = new Set(); // 初始化 entryInChunks 集合
}
for (const chunk of oldCgm.entryInChunks) { // 遍历旧模块是入口模块的 chunk 集合
const cgc = this._getChunkGraphChunk(chunk); // 获取 chunk 对应的 ChunkGraphChunk
const old = /** @type {Entrypoint} */ (cgc.entryModules.get(oldModule)); // 获取旧模块对应的入口配置
/** @type {Map<Module, Entrypoint>} */
const newEntryModules = new Map(); // 创建新的 entryModules 映射
for (const [m, cg] of cgc.entryModules) { // 遍历原有的 entryModules 映射
if (m === oldModule) { // 如果是旧模块
newEntryModules.set(newModule, old); // 替换为新模块
} else {
newEntryModules.set(m, cg); // 否则保持原样
}
}
cgc.entryModules = newEntryModules; // 替换 entryModules
newCgm.entryInChunks.add(chunk); // 将该 chunk 添加到新模块的 entryInChunks 集合
}
oldCgm.entryInChunks = undefined; // 清空旧模块的 entryInChunks
}
if (oldCgm.runtimeInChunks !== undefined) { // 如果旧模块被当作 runtime module 使用
if (newCgm.runtimeInChunks === undefined) { // 如果新模块没有 runtimeInChunks 属性
newCgm.runtimeInChunks = new Set(); // 初始化 runtimeInChunks 集合
}
for (const chunk of oldCgm.runtimeInChunks) { // 遍历旧模块所在的 runtime chunk
const cgc = this._getChunkGraphChunk(chunk); // 获取 chunk 对应的 ChunkGraphChunk
cgc.runtimeModules.delete(/** @type {RuntimeModule} */ (oldModule)); // 从 runtimeModules 中移除旧模块
cgc.runtimeModules.add(/** @type {RuntimeModule} */ (newModule)); // 添加新模块
newCgm.runtimeInChunks.add(chunk); // 将 chunk 添加到新模块的 runtimeInChunks
if (
cgc.fullHashModules !== undefined &&
cgc.fullHashModules.has(/** @type {RuntimeModule} */ (oldModule))
) { // 如果 chunk 的 fullHashModules 包含旧模块
cgc.fullHashModules.delete(/** @type {RuntimeModule} */ (oldModule)); // 删除旧模块
cgc.fullHashModules.add(/** @type {RuntimeModule} */ (newModule)); // 添加新模块
}
if (
cgc.dependentHashModules !== undefined &&
cgc.dependentHashModules.has(/** @type {RuntimeModule} */ (oldModule))
) { // 如果 chunk 的 dependentHashModules 包含旧模块
cgc.dependentHashModules.delete(
/** @type {RuntimeModule} */ (oldModule)
);
cgc.dependentHashModules.add(
/** @type {RuntimeModule} */ (newModule)
);
}
}
oldCgm.runtimeInChunks = undefined; // 清空旧模块的 runtimeInChunks
}
}
/**
* @param {Module} module the checked module
* @param {Chunk} chunk the checked chunk
* @returns {boolean} true, if the chunk contains the module
*/
isModuleInChunk(module, chunk) { // 判断模块是否存在于指定 chunk 中
const cgc = this._getChunkGraphChunk(chunk); // 获取 chunk 对应的 ChunkGraphChunk
return cgc.modules.has(module); // 判断模块集合中是否包含该模块
}
/**
* @param {Module} module the checked module
* @param {ChunkGroup} chunkGroup the checked chunk group
* @returns {boolean} true, if the chunk contains the module
*/
isModuleInChunkGroup(module, chunkGroup) { // 判断模块是否存在于指定 ChunkGroup 中
for (const chunk of chunkGroup.chunks) { // 遍历 chunkGroup 中的所有 chunk
if (this.isModuleInChunk(module, chunk)) return true; // 只要有一个 chunk 包含模块则返回 true
}
return false; // 所有 chunk 都不包含模块则返回 false
}
/**
* @param {Module} module the checked module
* @returns {boolean} true, if the module is entry of any chunk
*/
isEntryModule(module) { // 判断模块是否为入口模块
const cgm = this._getChunkGraphModule(module); // 获取 ChunkGraphModule
return cgm.entryInChunks !== undefined; // 判断是否存在 entryInChunks 集合
}
/**
* @param {Module} module the module
* @returns {Iterable<Chunk>} iterable of chunks (do not modify)
*/
getModuleChunksIterable(module) { // 获取模块所在的 chunk 集合(可迭代)
const cgm = this._getChunkGraphModule(module); // 获取 ChunkGraphModule
return cgm.chunks; // 返回 chunks 集合
}
/**
* @param {Module} module the module
* @param {function(Chunk, Chunk): -1|0|1} sortFn sort function
* @returns {Iterable<Chunk>} iterable of chunks (do not modify)
*/
getOrderedModuleChunksIterable(module, sortFn) { // 获取排序后的模块 chunk 集合(可迭代)
const cgm = this._getChunkGraphModule(module); // 获取 ChunkGraphModule
cgm.chunks.sortWith(sortFn); // 使用传入的排序函数进行排序
return cgm.chunks; // 返回排序后的 chunks 集合
}
/**
* @param {Module} module the module
* @returns {Chunk[]} array of chunks (cached, do not modify)
*/
getModuleChunks(module) { // 获取模块所在的 chunk 数组(使用缓存)
const cgm = this._getChunkGraphModule(module); // 获取 ChunkGraphModule
return cgm.chunks.getFromCache(getArray); // 从缓存中获取数组结果
}
/**
* @param {Module} module the module
* @returns {number} the number of chunk which contain the module
*/
getNumberOfModuleChunks(module) { // 获取包含该模块的 chunk 数量
const cgm = this._getChunkGraphModule(module); // 获取 ChunkGraphModule
return cgm.chunks.size; // 返回 chunk 集合的大小
}
/**
* @param {Module} module the module
* @returns {RuntimeSpecSet} runtimes
*/
getModuleRuntimes(module) { // 获取模块关联的运行时环境集合
const cgm = this._getChunkGraphModule(module); // 获取 ChunkGraphModule
return cgm.chunks.getFromUnorderedCache(getModuleRuntimes); // 从缓存中获取运行时集合
}
/**
* @param {Chunk} chunk the chunk
* @returns {number} the number of modules which are contained in this chunk
*/
getNumberOfChunkModules(chunk) { // 获取 chunk 中的模块数量
const cgc = this._getChunkGraphChunk(chunk); // 获取 ChunkGraphChunk
return cgc.modules.size; // 返回模块集合大小
}
/**
* @param {Chunk} chunk the chunk
* @returns {number} the number of full hash modules which are contained in this chunk
*/
getNumberOfChunkFullHashModules(chunk) { // 获取参与 chunk full hash 的模块数量
const cgc = this._getChunkGraphChunk(chunk); // 获取 ChunkGraphChunk
return cgc.fullHashModules === undefined ? 0 : cgc.fullHashModules.size; // 返回集合大小或 0
}
/**
* @param {Chunk} chunk the chunk
* @returns {Iterable<Module>} return the modules for this chunk
*/
getChunkModulesIterable(chunk) { // 获取 chunk 中模块的可迭代集合
const cgc = this._getChunkGraphChunk(chunk); // 获取 ChunkGraphChunk
return cgc.modules; // 返回模块集合
}
/**
* @param {Chunk} chunk the chunk
* @param {string} sourceType source type
* @returns {Iterable<Module> | undefined} return the modules for this chunk
*/
getChunkModulesIterableBySourceType(chunk, sourceType) { // 获取指定 sourceType 的模块集合
const cgc = this._getChunkGraphChunk(chunk); // 获取 ChunkGraphChunk
const modulesWithSourceType = cgc.modules // 从缓存中获取按 sourceType 分类的模块集合
.getFromUnorderedCache(cgc._modulesBySourceType)
.get(sourceType); // 获取指定类型的模块集合
return modulesWithSourceType; // 返回结果
}