webpack 检出图 第 十 节 lib/ChunkGraph.js

_getModuleGraphHashWithConnections(cgm, module, runtime)

  • 作用

    生成指定模块在某个 runtime 下,连同其"连接关系"后的唯一图结构哈希。

  • 输入参数

    • cgm:该模块在 ChunkGraph 中的封装对象(ChunkGraphModule)。
    • module:待处理的模块。
    • runtime:当前的 runtime,可为字符串或对象。
  • 主要逻辑

    • 初始化缓存结构 graphHashesWithConnections,基于 runtime 进行缓存。

    • 生成基础图哈希(不包含连接信息)。

    • 遍历模块的所有出边连接(Outgoing Connections):

      • 根据连接的激活状态(true / false / TRANSITIVE_ONLY)和模块的导出类型,生成唯一的 stateInfo 字符串。
      • 按照 stateInfo 分类收集连接模块到 connectedModules Map。
      • 特殊处理命名空间导出模块(Tnamespace),加入 activeNamespaceModules
    • 如果是多 runtime,则分别遍历各个 runtime 合成状态信息。

    • 如果没有有效连接,直接返回基本图哈希。

    • connectedModules 做排序,保证哈希的一致性。

    • 创建哈希对象并依次加入:

      • 命名空间模块的哈希(1个直接加,多个做异或合并)。
      • 每组连接模块的哈希(按 stateInfo 和模块内容)。
      • 原始模块的基本图哈希。
    • 返回最终的十六进制哈希字符串。


getTreeRuntimeRequirements(chunk)

  • 作用

    获取某个 chunk 所属的模块树(包含 chunk 的依赖模块)所需的 runtime requirements。

  • 输入参数

    • chunk:目标 chunk 对象。
  • 主要逻辑

    • 通过 _getChunkGraphChunk(chunk) 获取封装的 ChunkGraphChunk
    • 读取并返回其 runtimeRequirementsInTree 属性。
  • 用途

    • 判断生成的代码中是否需要引入某些 runtime 工具函数。
    • 用于生成 runtime 模块或注入 runtime helpers。
js 复制代码
/**
 * 获取某个模块在指定 runtime 下,连同其连接信息后的图哈希值。
 * @param {ChunkGraphModule} cgm 当前模块在 ChunkGraph 中的表示
 * @param {Module} module 当前处理的模块
 * @param {RuntimeSpec} runtime 当前的 runtime 规格(可以是字符串或对象)
 * @returns {string} 哈希值
 */
_getModuleGraphHashWithConnections(cgm, module, runtime) {
	// 初始化用于缓存哈希值的 Map(按 runtime 区分)
	if (cgm.graphHashesWithConnections === undefined) {
		cgm.graphHashesWithConnections = new RuntimeSpecMap();
	}

	/**
	 * 将模块连接的状态转换为字符形式。
	 * @param {ConnectionState} state 连接状态
	 * @returns {"F" | "T" | "O"} 字符化的状态
	 */
	const activeStateToString = state => {
		if (state === false) return "F"; // 不活跃
		if (state === true) return "T";  // 活跃
		if (state === ModuleGraphConnection.TRANSITIVE_ONLY) return "O"; // 仅传递连接
		throw new Error("Not implemented active state"); // 未知状态抛错
	};

	// 判断该模块是否是严格的 Harmony 模块
	const strict = module.buildMeta && module.buildMeta.strictHarmonyModule;

	// 提供 runtime 缓存处理逻辑
	return cgm.graphHashesWithConnections.provide(runtime, () => {
		// 获取当前模块图的基本哈希(BigInt → 十六进制字符串)
		const graphHash = this._getModuleGraphHashBigInt(
			cgm,
			module,
			runtime
		).toString(16);

		// 获取当前模块的所有出边连接
		const connections = this.moduleGraph.getOutgoingConnections(module);

		/** 用于记录活跃 namespace 模块 */
		const activeNamespaceModules = new Set();

		/** 记录连接模块的信息,key 是 stateInfo,value 是模块或模块集合 */
		const connectedModules = new Map();

		/**
		 * 处理一个连接,依据状态标识将其存入 map 中
		 * @param {ModuleGraphConnection} connection 连接对象
		 * @param {string} stateInfo 编码后的连接状态
		 */
		const processConnection = (connection, stateInfo) => {
			const module = connection.module;
			// 拼接上导出类型信息
			stateInfo += module.getExportsType(this.moduleGraph, strict);
			// 如果是命名空间模块,放入集合中
			if (stateInfo === "Tnamespace") activeNamespaceModules.add(module);
			else {
				const oldModule = connectedModules.get(stateInfo);
				if (oldModule === undefined) {
					// 首次出现,直接存入
					connectedModules.set(stateInfo, module);
				} else if (oldModule instanceof Set) {
					// 如果已有 Set,加入 Set
					oldModule.add(module);
				} else if (oldModule !== module) {
					// 如果已有的不是当前模块,则变成 Set
					connectedModules.set(stateInfo, new Set([oldModule, module]));
				}
			}
		};

		// 如果 runtime 是 undefined 或字符串(单一 runtime)
		if (runtime === undefined || typeof runtime === "string") {
			for (const connection of connections) {
				const state = connection.getActiveState(runtime);
				if (state === false) continue; // 忽略不活跃连接
				processConnection(connection, state === true ? "T" : "O");
			}
		} else {
			// 多 runtime 情况下,对每个 runtime 单独处理连接状态
			for (const connection of connections) {
				const states = new Set();
				let stateInfo = "";
				forEachRuntime(
					runtime,
					runtime => {
						const state = connection.getActiveState(runtime);
						states.add(state);
						stateInfo += activeStateToString(state) + runtime;
					},
					true
				);
				// 如果所有 runtime 的状态一致,可以简化为单状态
				if (states.size === 1) {
					const state = first(states);
					if (state === false) continue;
					stateInfo = activeStateToString(state);
				}
				processConnection(connection, stateInfo);
			}
		}

		// 如果没有任何连接模块,也没有命名空间模块,直接返回基本哈希
		if (activeNamespaceModules.size === 0 && connectedModules.size === 0)
			return graphHash;

		// 如果连接模块多于 1 个,则对 key 排序,确保哈希顺序一致性
		const connectedModulesInOrder =
			connectedModules.size > 1
				? Array.from(connectedModules).sort(([a], [b]) => (a < b ? -1 : 1))
				: connectedModules;

		// 创建哈希对象
		const hash = createHash(this._hashFunction);

		/**
		 * 将某个模块的哈希值写入 hash
		 * @param {Module} module 模块
		 */
		const addModuleToHash = module => {
			hash.update(
				this._getModuleGraphHashBigInt(
					this._getChunkGraphModule(module),
					module,
					runtime
				).toString(16)
			);
		};

		/**
		 * 将一组模块的哈希值进行异或后写入 hash
		 * @param {Set<Module>} modules 模块集合
		 */
		const addModulesToHash = modules => {
			let xor = ZERO_BIG_INT;
			for (const m of modules) {
				xor =
					xor ^
					this._getModuleGraphHashBigInt(
						this._getChunkGraphModule(m),
						m,
						runtime
					);
			}
			hash.update(xor.toString(16));
		};

		// 添加命名空间模块的哈希(单个直接加,多个异或加)
		if (activeNamespaceModules.size === 1)
			addModuleToHash(
				/** @type {Module} */ (activeNamespaceModules.values().next().value)
			);
		else if (activeNamespaceModules.size > 1)
			addModulesToHash(activeNamespaceModules);

		// 添加连接模块的哈希
		for (const [stateInfo, modules] of connectedModulesInOrder) {
			hash.update(stateInfo);
			if (modules instanceof Set) {
				addModulesToHash(modules);
			} else {
				addModuleToHash(modules);
			}
		}

		// 最后拼上原始模块的图哈希
		hash.update(graphHash);

		// 输出最终哈希结果
		return /** @type {string} */ (hash.digest("hex"));
	});
}

/**
 * 获取一个 chunk 及其依赖模块树的 runtime 需求(runtime requirements)。
 * 这些 runtime requirements 通常是为确保模块加载与执行所需的运行时代码(如 __webpack_require__, chunk loading 代码等)。
 * 
 * @param {Chunk} chunk 要处理的 chunk 实例
 * @returns {ReadOnlyRuntimeRequirements} 该 chunk 的只读 runtime 需求集合
 */
getTreeRuntimeRequirements(chunk) {
	// 获取 ChunkGraphChunk 实例(这是 Chunk 在 ChunkGraph 中的封装,保存额外信息)
	const cgc = this._getChunkGraphChunk(chunk);

	// 返回该 chunk 所在模块树的 runtime 需求集合
	return cgc.runtimeRequirementsInTree;
}
相关推荐
程序猿小玉兒2 分钟前
若依框架免登陆、页面全屏显示、打开新标签页(看板大屏)
前端
小薛博客21 分钟前
3、整合前端基础交互页面
java·前端·ai·交互
@蓝莓果粒茶26 分钟前
LeetCode第158题_用Read4读取N个字符 II
前端·c++·python·算法·leetcode·职场和发展·c#
天天扭码27 分钟前
【硬核教程】从入门到入土!彻底吃透 JavaScript 中 this 关键字这一篇就够了
前端·javascript·面试
Mintopia1 小时前
计算机图形学学习指南
前端·javascript·计算机图形学
Mintopia1 小时前
three.js 中的动画(animation)
前端·javascript·three.js
AI大模型顾潇1 小时前
[特殊字符] Prompt如何驱动大模型对本地文件实现自主变更:Cline技术深度解析
前端·人工智能·llm·微调·prompt·编程·ai大模型
小小小小宇1 小时前
React中 useEffect和useLayoutEffect源码原理
前端
AlexJee1 小时前
在vue3中使用vue-cropper完成头像裁剪上传图片功能
前端
清晨細雨1 小时前
uniapp微信小程序:WIFI设备配网之TCP/UDP开发AP配网
前端·物联网·小程序·uni-app