webpack 检出图 第 一 节

✅ 类型定义与常量

  • 定义了多个模块类型、结构、函数签名等,用于 JSDoc 提示。

  • 常量:

    • EMPTY_SET:空集合,常用于默认值。
    • ZERO_BIG_INT:BigInt 形式的 0。
    • compareModuleIterables:模块排序器。

✅ 类型别名定义

  • ChunkFilterPredicate:用于过滤 chunk 的函数类型。
  • ModuleFilterPredicate:用于过滤 module 的函数类型。
  • EntryModuleWithChunkGroup:模块和它对应入口的元组。
  • ChunkSizeOptions:chunk 体积计算参数。

✅ 工具类

  • ModuleHashInfo

    • 存储模块的 hash 和渲染后的 hash。
    • 用于构建缓存和是否重新编译判断。

✅ 工具函数

  • getArray(set):将 SortableSet 转换为普通数组。

  • getModuleRuntimes(chunks):获取 chunk 集合中的所有 runtime。

  • modulesBySourceType(sourceTypesByModule)

    • 把模块集合按 sourceType 分类。
    • 可重用原集合,优化排序缓存。
  • createOrderedArrayFunction(comparator)

    • 创建一个集合转排序数组的函数,并做缓存。
  • getModulesSize(modules):计算所有模块的总大小(所有类型总和)。

  • getModulesSizes(modules):按 sourceType 分类统计每类大小。

  • isAvailableChunk(a, b)

    • 判断 chunk a 是否一定是 chunk b 的父 chunk。
    • 用于 chunk 之间的依赖关系判断。

✅ ChunkGraphModule 类

  • chunks:模块所属的 chunk 集合。
  • entryInChunks:如果模块是入口模块,则保存相关 chunk。
  • runtimeInChunks:如果模块是 runtime 模块,则保存相关 chunk。
  • hashes:不同 runtime 下的模块 hash 信息。
  • id:模块的输出 ID。
  • runtimeRequirements:各 runtime 下的 runtime 依赖。
  • graphHashes:模块图结构的 hash。
  • graphHashesWithConnections:模块图+依赖连接信息的 hash(更严格)。
js 复制代码
// ==== 类型导入(用于 JSDoc 类型提示) ====

/** @typedef {import("./AsyncDependenciesBlock")} AsyncDependenciesBlock */
/** @typedef {import("./Chunk")} Chunk */
/** @typedef {import("./Chunk").ChunkId} ChunkId */
/** @typedef {import("./ChunkGroup")} ChunkGroup */
/** @typedef {import("./Generator").SourceTypes} SourceTypes */
/** @typedef {import("./Module")} Module */
/** @typedef {import("./Module").ReadOnlyRuntimeRequirements} ReadOnlyRuntimeRequirements */
/** @typedef {import("./ModuleGraph")} ModuleGraph */
/** @typedef {import("./ModuleGraphConnection").ConnectionState} ConnectionState */
/** @typedef {import("./RuntimeModule")} RuntimeModule */
/** @typedef {typeof import("./util/Hash")} Hash */
/** @typedef {import("./util/runtime").RuntimeSpec} RuntimeSpec */

// ==== 常量定义 ====

/** 空集合常量(不可变),可用于默认值等 */
const EMPTY_SET = new Set();

/** 零值的 BigInt 常量 */
const ZERO_BIG_INT = BigInt(0);

/** 比较模块集合的工具函数,用于排序 */
const compareModuleIterables = compareIterables(compareModulesByIdentifier);

// ==== 类型别名定义 ====

/**
 * 过滤 chunk 的函数类型定义
 * @typedef {(c: Chunk, chunkGraph: ChunkGraph) => boolean} ChunkFilterPredicate
 */

/**
 * 过滤模块的函数类型定义
 * @typedef {(m: Module) => boolean} ModuleFilterPredicate
 */

/**
 * 模块和它所属的入口 chunk group 对(可能 undefined)
 * @typedef {[Module, Entrypoint | undefined]} EntryModuleWithChunkGroup
 */

/**
 * 表示计算 chunk 大小时的一些配置参数
 * @typedef {object} ChunkSizeOptions
 * @property {number=} chunkOverhead chunk 的固定开销
 * @property {number=} entryChunkMultiplicator 入口 chunk 的权重乘数
 */

// ==== 工具类 ====

/**
 * 模块的 hash 信息,用于缓存和优化重构
 */
class ModuleHashInfo {
	/**
	 * @param {string} hash 模块构建产物的 hash
	 * @param {string} renderedHash 渲染后的 hash,用于输出文件名等
	 */
	constructor(hash, renderedHash) {
		this.hash = hash;
		this.renderedHash = renderedHash;
	}
}

// ==== 工具函数 ====

/**
 * 将 SortableSet 转为数组
 * @template T
 * @param {SortableSet<T>} set 可排序的集合
 * @returns {T[]} 转换后的数组
 */
const getArray = set => Array.from(set);

/**
 * 获取某些 chunk 所涉及的 runtime 集合(RuntimeSpecSet)
 * @param {SortableSet<Chunk>} chunks chunk 集合
 * @returns {RuntimeSpecSet} 所有 runtime 的集合
 */
const getModuleRuntimes = chunks => {
	const runtimes = new RuntimeSpecSet();
	for (const chunk of chunks) {
		runtimes.add(chunk.runtime);
	}
	return runtimes;
};

/**
 * 把模块集合根据 sourceType 分类
 * @param {WeakMap<Module, Set<string>> | undefined} sourceTypesByModule 提前缓存的模块 sourceType
 * @returns {function (SortableSet<Module>): Map<string, SortableSet<Module>>} 分类后的模块映射表
 */
const modulesBySourceType = sourceTypesByModule => set => {
	/** @type {Map<string, SortableSet<Module>>} */
	const map = new Map();
	for (const module of set) {
		const sourceTypes =
			(sourceTypesByModule && sourceTypesByModule.get(module)) ||
			module.getSourceTypes();
		for (const sourceType of sourceTypes) {
			let innerSet = map.get(sourceType);
			if (innerSet === undefined) {
				innerSet = new SortableSet();
				map.set(sourceType, innerSet);
			}
			innerSet.add(module);
		}
	}
	for (const [key, innerSet] of map) {
		// 如果所有模块都包含该 sourceType,就直接复用原集合,提高排序缓存效率
		if (innerSet.size === set.size) {
			map.set(key, set);
		}
	}
	return map;
};

// 默认情况下不提供预缓存,直接调用模块的 getSourceTypes 方法
const defaultModulesBySourceType = modulesBySourceType(undefined);

/**
 * 创建一个能排序集合并转数组的函数,并缓存该函数避免重复生成
 * @template T
 * @type {WeakMap<Function, any>}
 */
const createOrderedArrayFunctionMap = new WeakMap();

/**
 * 返回一个函数,该函数能将集合排序后转成数组
 * @template T
 * @param {function(T, T): -1|0|1} comparator 排序比较器
 * @returns {SetToArrayFunction<T>} 生成的转换函数
 */
const createOrderedArrayFunction = comparator => {
	let fn = createOrderedArrayFunctionMap.get(comparator);
	if (fn !== undefined) return fn;
	fn = set => {
		set.sortWith(comparator);
		return Array.from(set);
	};
	createOrderedArrayFunctionMap.set(comparator, fn);
	return fn;
};

/**
 * 计算模块总大小(所有 sourceType 加起来)
 * @param {Iterable<Module>} modules 模块集合
 * @returns {number} 模块总大小
 */
const getModulesSize = modules => {
	let size = 0;
	for (const module of modules) {
		for (const type of module.getSourceTypes()) {
			size += module.size(type);
		}
	}
	return size;
};

/**
 * 分 sourceType 统计模块大小
 * @param {Iterable<Module>} modules 模块集合
 * @returns {Record<string, number>} 每种类型的大小
 */
const getModulesSizes = modules => {
	const sizes = Object.create(null);
	for (const module of modules) {
		for (const type of module.getSourceTypes()) {
			sizes[type] = (sizes[type] || 0) + module.size(type);
		}
	}
	return sizes;
};

/**
 * 判断 chunk `a` 是否是 chunk `b` 的父 chunk
 * @param {Chunk} a 父 chunk
 * @param {Chunk} b 子 chunk
 * @returns {boolean} 如果 a 是 b 的父 chunk,返回 true
 */
const isAvailableChunk = (a, b) => {
	const queue = new Set(b.groupsIterable);
	for (const chunkGroup of queue) {
		if (a.isInGroup(chunkGroup)) continue;
		if (chunkGroup.isInitial()) return false;
		for (const parent of chunkGroup.parentsIterable) {
			queue.add(parent);
		}
	}
	return true;
};

// ==== 类型别名:用于 ChunkGraphModule 中属性类型标注 ====

/** @typedef {Set<Chunk>} EntryInChunks */
/** @typedef {Set<Chunk>} RuntimeInChunks */
/** @typedef {string | number} ModuleId */

// ==== ChunkGraph 中用于存储模块信息的结构 ====

class ChunkGraphModule {
	constructor() {
		/** @type {SortableSet<Chunk>} 模块所属的 chunk 集合 */
		this.chunks = new SortableSet();

		/** @type {EntryInChunks | undefined} 如果模块是 entry 模块,这里存放其所在的入口 chunk 集合 */
		this.entryInChunks = undefined;

		/** @type {RuntimeInChunks | undefined} 如果模块是 runtime 模块,这里存放其对应 chunk */
		this.runtimeInChunks = undefined;

		/** @type {RuntimeSpecMap<ModuleHashInfo> | undefined} 各 runtime 对应的 hash 信息 */
		this.hashes = undefined;

		/** @type {ModuleId | null} 模块 ID(用于输出) */
		this.id = null;

		/** @type {RuntimeSpecMap<Set<string>> | undefined} 各 runtime 所依赖的 runtime helper */
		this.runtimeRequirements = undefined;

		/** @type {RuntimeSpecMap<string> | undefined} 模块图结构的 hash */
		this.graphHashes = undefined;

		/** @type {RuntimeSpecMap<string> | undefined} 模块图 + 连接信息 的 hash(用于更精确地判断是否变化) */
		this.graphHashesWithConnections = undefined;
	}
}
相关推荐
沙振宇2 小时前
【Web】使用Vue3开发鸿蒙的HelloWorld!
前端·华为·harmonyos
运维@小兵3 小时前
vue开发用户注册功能
前端·javascript·vue.js
蓝婷儿3 小时前
前端面试每日三题 - Day 30
前端·面试·职场和发展
oMMh3 小时前
使用C# ASP.NET创建一个可以由服务端推送信息至客户端的WEB应用(2)
前端·c#·asp.net
一口一个橘子3 小时前
[ctfshow web入门] web69
前端·web安全·网络安全
读心悦4 小时前
CSS:盒子阴影与渐变完全解析:从基础语法到创意应用
前端·css
湛海不过深蓝6 小时前
【ts】defineProps数组的类型声明
前端·javascript·vue.js
layman05286 小时前
vue 中的数据代理
前端·javascript·vue.js
柒七爱吃麻辣烫6 小时前
前端项目打包部署流程j
前端
layman05286 小时前
vue中理解MVVM
前端·javascript·vue.js