_codeGenerationModule
功能 :
执行模块的代码生成,并利用缓存优化性能。
- 先检查缓存是否有已生成的代码,如果有,则直接使用。
- 如果缓存中没有,则执行代码生成,并存储到缓存中。
- 代码生成可能会失败,如果失败,则记录错误并返回空结果。
- 生成的代码会存储在
results
变量中,并与多个运行时关联。
关键点:
- 代码生成的核心逻辑依赖
module.codeGeneration
方法。 - 结果会缓存,以避免重复生成,提高性能。
- 通过
callback
处理异步执行的流程。
js
/**
* 代码生成模块
*
* 该函数用于执行模块的代码生成,并利用缓存来优化性能。
* 如果缓存中有结果,则直接使用,否则执行代码生成过程,并存储结果到缓存中。
*
* @param {Module} module - 需要生成代码的模块
* @param {RuntimeSpec} runtime - 当前运行时
* @param {RuntimeSpec[]} runtimes - 所有运行时
* @param {string} hash - 代码哈希值
* @param {DependencyTemplates} dependencyTemplates - 依赖模板
* @param {ChunkGraph} chunkGraph - Chunk 图
* @param {ModuleGraph} moduleGraph - 模块图
* @param {RuntimeTemplate} runtimeTemplate - 运行时模板
* @param {WebpackError[]} errors - 错误列表
* @param {CodeGenerationResults} results - 代码生成结果
* @param {function((WebpackError | null)=, boolean=): void} callback - 回调函数
*/
_codeGenerationModule(
module,
runtime,
runtimes,
hash,
dependencyTemplates,
chunkGraph,
moduleGraph,
runtimeTemplate,
errors,
results,
callback
) {
// 代码生成标志
let codeGenerated = false;
// 创建多项缓存
const cache = new MultiItemCache(
runtimes.map(runtime =>
this._codeGenerationCache.getItemCache(
`${module.identifier()}|${getRuntimeKey(runtime)}`,
`${hash}|${dependencyTemplates.getHash()}`
)
)
);
// 从缓存获取结果
cache.get((err, cachedResult) => {
if (err) return callback(/** @type {WebpackError} */(err));
let result;
if (!cachedResult) {
try {
// 代码生成
codeGenerated = true;
this.codeGeneratedModules.add(module);
result = module.codeGeneration({
chunkGraph,
moduleGraph,
dependencyTemplates,
runtimeTemplate,
runtime,
codeGenerationResults: results,
compilation: this
});
} catch (err) {
// 代码生成失败,记录错误
errors.push(
new CodeGenerationError(module, /** @type {Error} */(err))
);
result = cachedResult = {
sources: new Map(),
runtimeRequirements: null
};
}
} else {
result = cachedResult;
}
// 存储结果
for (const runtime of runtimes) {
results.add(module, runtime, result);
}
// 将结果存入缓存
if (!cachedResult) {
cache.store(result, err =>
callback(/** @type {WebpackError} */(err), codeGenerated)
);
} else {
callback(null, codeGenerated);
}
});
}
_getChunkGraphEntries
功能 :
获取 ChunkGraph
的入口 Chunk
集合。
- 遍历
this.entrypoints
和this.asyncEntrypoints
,获取它们对应的RuntimeChunk
。 - 这些
RuntimeChunk
代表 Webpack 入口点(同步和异步)。 - 结果是一个
Set
,包含所有的入口Chunk
。
关键点:
- 主要用于后续计算
ChunkGraph
相关的运行时需求。 - 入口
Chunk
是 Webpack 执行的起点,对运行时至关重要。
js
/**
* 获取 Chunk 图的入口点
*
* 该函数返回一个包含所有入口 `Chunk` 的集合,包括同步和异步入口点。
*
* @returns {Set<Chunk>} - 入口 Chunk 集合
*/
_getChunkGraphEntries() {
const treeEntries = new Set();
for (const ep of this.entrypoints.values()) {
const chunk = ep.getRuntimeChunk();
if (chunk) treeEntries.add(chunk);
}
for (const ep of this.asyncEntrypoints) {
const chunk = ep.getRuntimeChunk();
if (chunk) treeEntries.add(chunk);
}
return treeEntries;
}
processRuntimeRequirements
功能 :
计算 Webpack 运行时需求,包括模块、Chunk 和入口点。
-
模块的运行时需求:
- 遍历所有模块,检查它们是否存在于
ChunkGraph
中。 - 计算模块的运行时需求,可能会调用
hooks.additionalModuleRuntimeRequirements
进行增强。 - 结果存储在
chunkGraph
中。
- 遍历所有模块,检查它们是否存在于
-
Chunk 的运行时需求:
- 遍历所有 Chunk,收集其中所有模块的运行时需求。
- 结果存储在
chunkGraph
,以便运行时可以正确加载模块。
-
入口 Chunk 的运行时需求:
- 计算
ChunkGraph
入口点的所有依赖项。 - 通过
hooks
进一步处理,确保正确的运行时加载顺序。
- 计算
关键点:
- 通过
hooks
允许插件扩展 Webpack 的运行时行为。 - 使用
chunkGraph
作为核心数据结构,管理模块和 Chunk 的关系。 - 运行时需求是 Webpack 生成最终打包代码的重要组成部分。
js
/**
* 处理运行时需求
*
* 该函数用于处理 Webpack 运行时需求,包括:
* 1. 计算模块的运行时需求
* 2. 计算 Chunk 的运行时需求
* 3. 计算入口 Chunk 的运行时需求
*
* 通过 `hooks` 触发不同阶段的处理,并使用缓存优化性能。
*
* @param {object} options - 选项对象
* @param {ChunkGraph=} options.chunkGraph - Chunk 图
* @param {Iterable<Module>=} options.modules - 需要处理的模块
* @param {Iterable<Chunk>=} options.chunks - 需要处理的 Chunk
* @param {CodeGenerationResults=} options.codeGenerationResults - 代码生成结果
* @param {Iterable<Chunk>=} options.chunkGraphEntries - Chunk 图的入口点
* @returns {void}
*/
processRuntimeRequirements({
chunkGraph = this.chunkGraph,
modules = this.modules,
chunks = this.chunks,
codeGenerationResults = this.codeGenerationResults,
chunkGraphEntries = this._getChunkGraphEntries()
} = {}) {
const context = { chunkGraph, codeGenerationResults };
const { moduleMemCaches2 } = this;
// 处理模块的运行时需求
this.logger.time("runtime requirements.modules");
const additionalModuleRuntimeRequirements =
this.hooks.additionalModuleRuntimeRequirements;
const runtimeRequirementInModule = this.hooks.runtimeRequirementInModule;
for (const module of modules) {
if (chunkGraph.getNumberOfModuleChunks(module) > 0) {
const memCache = moduleMemCaches2 && moduleMemCaches2.get(module);
for (const runtime of chunkGraph.getModuleRuntimes(module)) {
let set;
const runtimeRequirements =
codeGenerationResults.getRuntimeRequirements(module, runtime);
if (runtimeRequirements && runtimeRequirements.size > 0) {
set = new Set(runtimeRequirements);
} else if (additionalModuleRuntimeRequirements.isUsed()) {
set = new Set();
} else {
continue;
}
additionalModuleRuntimeRequirements.call(module, set, context);
for (const r of set) {
const hook = runtimeRequirementInModule.get(r);
if (hook !== undefined) hook.call(module, set, context);
}
chunkGraph.addModuleRuntimeRequirements(module, runtime, set);
}
}
}
this.logger.timeEnd("runtime requirements.modules");
// 处理 Chunk 的运行时需求
this.logger.time("runtime requirements.chunks");
for (const chunk of chunks) {
const set = new Set();
for (const module of chunkGraph.getChunkModulesIterable(chunk)) {
const runtimeRequirements = chunkGraph.getModuleRuntimeRequirements(
module,
chunk.runtime
);
for (const r of runtimeRequirements) set.add(r);
}
chunkGraph.addChunkRuntimeRequirements(chunk, set);
}
this.logger.timeEnd("runtime requirements.chunks");
// 处理入口 Chunk 的运行时需求
this.logger.time("runtime requirements.entries");
for (const treeEntry of chunkGraphEntries) {
const set = new Set();
for (const chunk of treeEntry.getAllReferencedChunks()) {
const runtimeRequirements =
chunkGraph.getChunkRuntimeRequirements(chunk);
for (const r of runtimeRequirements) set.add(r);
}
chunkGraph.addTreeRuntimeRequirements(treeEntry, set);
}
this.logger.timeEnd("runtime requirements.entries");
}
addRuntimeModule
功能 :
向 Chunk
添加 RuntimeModule
,并建立它与 ChunkGraph
之间的关联。
- 将
RuntimeModule
添加到 Webpackthis.modules
列表中。 - 在
ChunkGraph
中建立Chunk
与RuntimeModule
的连接关系。 - 处理
RuntimeModule
的哈希信息,确保哈希一致性。 - 设置
exportsInfo
,用于跟踪模块的导出信息。 - 添加 Webpack 运行时需求 (
RuntimeGlobals.requireScope
)。 - 触发
this.hooks.runtimeModule
,允许插件扩展RuntimeModule
处理逻辑。
关键点:
RuntimeModule
负责 Webpack 运行时的一些核心逻辑,如模块加载、HMR(热模块替换)等。- 这个函数确保
RuntimeModule
正确关联到ChunkGraph
,使其能在打包后的代码中正确执行。 - Webpack 6 计划强制要求
chunkGraph
参数,以增强 API 规范性。
js
/**
* 向 Chunk 添加运行时模块
*
* 该函数用于将 `RuntimeModule` 添加到指定 `Chunk` 中,并建立其与 `ChunkGraph` 的关联:
* 1. 将模块添加到 `this.modules` 列表
* 2. 在 `ChunkGraph` 中建立模块与 Chunk 之间的关系
* 3. 处理模块的哈希信息
* 4. 设置模块的 `exports` 信息
* 5. 添加 Webpack 运行时需求
* 6. 触发 `runtimeModule` 钩子
*
* @param {Chunk} chunk - 目标 Chunk
* @param {RuntimeModule} module - 运行时模块
* @param {ChunkGraph} chunkGraph - Chunk 图(可选参数,默认使用 this.chunkGraph)
* @returns {void}
*/
addRuntimeModule(chunk, module, chunkGraph = this.chunkGraph) {
this.modules.add(module);
this._modules.set(module.identifier(), module);
chunkGraph.connectChunkAndModule(chunk, module);
chunkGraph.connectChunkAndRuntimeModule(chunk, module);
module.attach(this, chunk, chunkGraph);
chunkGraph.addModuleRuntimeRequirements(
module,
chunk.runtime,
new Set([RuntimeGlobals.requireScope])
);
chunkGraph.setModuleId(module, "");
this.hooks.runtimeModule.call(module, chunk);
}