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;
	}
}
相关推荐
于冬恋6 分钟前
Web后端开发(请求、响应)
前端
red润12 分钟前
封装hook,复刻掘金社区,暗黑白天主题切换功能
前端·javascript·vue.js
Fly-ping13 分钟前
【前端】vue3性能优化方案
前端·性能优化
curdcv_po14 分钟前
前端开发必要会的,在线JS混淆加密
前端
天生我材必有用_吴用16 分钟前
深入理解JavaScript设计模式之单例模式
前端
LuckySusu16 分钟前
【HTML篇】DOCTYPE 声明:掌握浏览器渲染模式的关键
前端·html
Darling哒17 分钟前
HTML块拖拽交换
前端
码农之王18 分钟前
(一)TypeScript概述和环境搭建
前端·后端·typescript
葬送的代码人生30 分钟前
React组件化哲学:如何优雅地"变秃也变强"
前端·javascript·react.js
用户527096487449031 分钟前
🚀 前端项目代码质量配置Prettier + Commitlint + Husky + Lint-staged
前端