webpack 检出图 第 二 节

🔧 构造函数初始化

  • 初始化 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 集合中。
  • 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);
		}
	}
}
相关推荐
CodeCraft Studio1 分钟前
数据透视表控件DHTMLX Pivot v2.1发布,新增HTML 模板、增强样式等多个功能
前端·javascript·ui·甘特图
一把年纪学编程4 分钟前
【牛马技巧】word统计每一段的字数接近“字数统计”
前端·数据库·word
llc的足迹13 分钟前
el-menu 折叠后小箭头不会消失
前端·javascript·vue.js
九月TTS43 分钟前
TTS-Web-Vue系列:移动端侧边栏与响应式布局深度优化
前端·javascript·vue.js
Johnstons1 小时前
AnaTraf:深度解析网络性能分析(NPM)
前端·网络·安全·web安全·npm·网络流量监控·网络流量分析
whatever who cares1 小时前
CSS3 伪元素(Pseudo-elements)大全
前端·css·css3
若愚67921 小时前
前端取经路——性能优化:唐僧的九道心经
前端·性能优化
Bl_a_ck2 小时前
开发环境(Development Environment)
开发语言·前端·javascript·typescript·ecmascript
田本初2 小时前
使用vite重构vue-cli的vue3项目
前端·vue.js·重构
ai产品老杨2 小时前
AI赋能安全生产,推进数智化转型的智慧油站开源了。
前端·javascript·vue.js·人工智能·ecmascript