webpack 核心编译器 十 节

_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.entrypointsthis.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 添加到 Webpack this.modules 列表中。
  • ChunkGraph 中建立 ChunkRuntimeModule 的连接关系。
  • 处理 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);
}
相关推荐
careybobo28 分钟前
海康摄像头通过Web插件进行预览播放和控制
前端
杉之2 小时前
常见前端GET请求以及对应的Spring后端接收接口写法
java·前端·后端·spring·vue
喝拿铁写前端2 小时前
字段聚类,到底有什么用?——从系统混乱到结构认知的第一步
前端
再学一点就睡2 小时前
大文件上传之切片上传以及开发全流程之前端篇
前端·javascript
木木黄木木3 小时前
html5炫酷图片悬停效果实现详解
前端·html·html5
请来次降维打击!!!4 小时前
优选算法系列(5.位运算)
java·前端·c++·算法
難釋懷4 小时前
JavaScript基础-移动端常见特效
开发语言·前端·javascript
自动花钱机5 小时前
WebUI问题总结
前端·javascript·bootstrap·css3·html5
拉不动的猪5 小时前
简单回顾下pc端与mobile端的适配问题
前端·javascript·面试
拉不动的猪5 小时前
刷刷题49(react中几个常见的性能优化问题)
前端·react.js·面试