webpack 模块 第 四 节

这段代码是 Webpack 中模块系统的基类 Module 的实现,提供了模块在整个构建生命周期中的核心功能接口。它定义了一套通用的逻辑,用于管理模块的构建状态、错误和警告收集、模块间依赖关系分析、哈希更新,以及可访问性和可选性判断。许多功能在构建过程、优化阶段(如 Tree-shaking)、模块缓存判断等场景中扮演重要角色。

首先,Module 类提供了完整的错误与警告管理方法,例如 addWarningaddErrorgetWarningsgetErrors 等,能够记录并查询模块在构建过程中的诊断信息。通过这些接口,Webpack 能够在编译报告中准确输出每个模块的构建问题。

其次,类中包含多个判断模块是否被其他模块依赖的方法,如 hasReasonsisOptional,这对于优化和判断模块是否可以被移除至关重要。同时,它还提供了模块与 chunk 或 chunkGroup 的可访问性分析方法,例如 isAccessibleInChunkhasReasonForChunk,用于判断模块是否需要包含在特定输出中,是代码分割和动态导入机制的基础。

在构建方面,Module 提供了 needBuildneedRebuild 方法,用于判断模块是否需要重新构建。模块更新时的哈希计算逻辑由 updateHash 提供,这有助于判断模块是否发生了变化,从而支持缓存机制和增量构建。

此外,该类还定义了一些必须由子类实现的抽象方法,如 identifierreadableIdentifierbuild,以支持不同类型模块的具体逻辑扩展。整个 Module 基类为 Webpack 的模块机制提供了统一的行为定义和扩展接口,是模块构建系统的核心组成部分。

js 复制代码
/**
 * 添加一个警告信息。
 * 如果 _warnings 还未初始化,则先初始化为一个空数组。
 * 然后将传入的 warning 添加到该数组中。
 * @param {WebpackError} warning 警告对象
 */
addWarning(warning) {
	if (this._warnings === undefined) {
		this._warnings = [];
	}
	this._warnings.push(warning);
}

/**
 * 获取当前模块的所有警告信息。
 * @returns {Iterable<WebpackError> | undefined} 警告数组或 undefined(如果没有)
 */
getWarnings() {
	return this._warnings;
}

/**
 * 获取当前模块的警告数量。
 * @returns {number} 警告数
 */
getNumberOfWarnings() {
	return this._warnings !== undefined ? this._warnings.length : 0;
}

/**
 * 添加一个错误信息。
 * 如果 _errors 还未初始化,则先初始化为一个空数组。
 * 然后将传入的 error 添加到该数组中。
 * @param {WebpackError} error 错误对象
 */
addError(error) {
	if (this._errors === undefined) {
		this._errors = [];
	}
	this._errors.push(error);
}

/**
 * 获取当前模块的所有错误信息。
 * @returns {Iterable<WebpackError> | undefined} 错误数组或 undefined(如果没有)
 */
getErrors() {
	return this._errors;
}

/**
 * 获取当前模块的错误数量。
 * @returns {number} 错误数
 */
getNumberOfErrors() {
	return this._errors !== undefined ? this._errors.length : 0;
}

/**
 * 清空当前模块的所有错误和警告信息。
 */
clearWarningsAndErrors() {
	if (this._warnings !== undefined) {
		this._warnings.length = 0;
	}
	if (this._errors !== undefined) {
		this._errors.length = 0;
	}
}

/**
 * 判断当前模块是否是"可选的"。
 * 仅当所有的 incoming 连接的依赖都是 optional 且激活时才算可选。
 * @param {ModuleGraph} moduleGraph 模块图
 * @returns {boolean} 是否为可选模块
 */
isOptional(moduleGraph) {
	let hasConnections = false;
	for (const r of moduleGraph.getIncomingConnections(this)) {
		if (
			!r.dependency ||
			!r.dependency.optional ||
			!r.isTargetActive(undefined)
		) {
			return false;
		}
		hasConnections = true;
	}
	return hasConnections;
}

/**
 * 判断模块是否在给定 chunk 中是可访问的(可排除某个 chunk)。
 * @param {ChunkGraph} chunkGraph chunk 图
 * @param {Chunk} chunk 当前 chunk
 * @param {Chunk=} ignoreChunk 要忽略的 chunk(可选)
 * @returns {boolean} 是否可访问
 */
isAccessibleInChunk(chunkGraph, chunk, ignoreChunk) {
	for (const chunkGroup of chunk.groupsIterable) {
		if (!this.isAccessibleInChunkGroup(chunkGraph, chunkGroup)) return false;
	}
	return true;
}

/**
 * 判断模块是否在某个 chunkGroup 中是可访问的(可排除某个 chunk)。
 * 采用广度优先队列遍历 chunk group 的所有父级,直到找到包含此模块的 chunk。
 * @param {ChunkGraph} chunkGraph chunk 图
 * @param {ChunkGroup} chunkGroup chunk 组
 * @param {Chunk=} ignoreChunk 要忽略的 chunk(可选)
 * @returns {boolean} 是否可访问
 */
isAccessibleInChunkGroup(chunkGraph, chunkGroup, ignoreChunk) {
	const queue = new Set([chunkGroup]);

	queueFor: for (const cg of queue) {
		for (const chunk of cg.chunks) {
			if (chunk !== ignoreChunk && chunkGraph.isModuleInChunk(this, chunk))
				continue queueFor;
		}
		if (chunkGroup.isInitial()) return false;
		for (const parent of chunkGroup.parentsIterable) queue.add(parent);
	}
	return true;
}

/**
 * 判断模块是否有"理由"存在于某个 chunk 中。
 * 一般用于 chunk 拆分或 tree-shaking 的逻辑。
 * @param {Chunk} chunk 当前 chunk
 * @param {ModuleGraph} moduleGraph 模块图
 * @param {ChunkGraph} chunkGraph chunk 图
 * @returns {boolean} 是否有理由包含在该 chunk 中
 */
hasReasonForChunk(chunk, moduleGraph, chunkGraph) {
	for (const [
		fromModule,
		connections
	] of moduleGraph.getIncomingConnectionsByOriginModule(this)) {
		if (!connections.some(c => c.isTargetActive(chunk.runtime))) continue;
		for (const originChunk of chunkGraph.getModuleChunksIterable(
			/** @type {Module} */ (fromModule)
		)) {
			if (!this.isAccessibleInChunk(chunkGraph, originChunk, chunk))
				return true;
		}
	}
	return false;
}

/**
 * 判断当前模块是否被任何其它模块依赖(即是否"有理由"存在)。
 * 常用于标记无依赖模块是否可删除。
 * @param {ModuleGraph} moduleGraph 模块图
 * @param {RuntimeSpec} runtime 当前运行时
 * @returns {boolean} 是否被依赖
 */
hasReasons(moduleGraph, runtime) {
	for (const c of moduleGraph.getIncomingConnections(this)) {
		if (c.isTargetActive(runtime)) return true;
	}
	return false;
}

/**
 * 返回模块的调试信息字符串。
 * @returns {string} 形如 `Module[debugId: identifier()]`
 */
toString() {
	return `Module[${this.debugId}: ${this.identifier()}]`;
}

/**
 * 判断是否需要重新构建模块(异步回调方式)。
 * @param {NeedBuildContext} context 构建上下文
 * @param {function((WebpackError | null)=, boolean=): void} callback 回调,返回是否需要构建
 */
needBuild(context, callback) {
	callback(
		null,
		!this.buildMeta ||
			this.needRebuild === Module.prototype.needRebuild ||
			deprecatedNeedRebuild(this, context)
	);
}

/**
 * (已废弃)判断是否需要重新构建模块(同步方式)。
 * 始终返回 true。
 * @deprecated 使用 needBuild 替代
 */
needRebuild(fileTimestamps, contextTimestamps) {
	return true;
}

/**
 * 更新模块的 hash 值,用于依赖追踪与缓存验证。
 * @param {Hash} hash 哈希对象
 * @param {UpdateHashContext} context 上下文信息(包括 chunkGraph 和 runtime)
 */
updateHash(
	hash,
	context = {
		chunkGraph: ChunkGraph.getChunkGraphForModule(
			this,
			"Module.updateHash",
			"DEP_WEBPACK_MODULE_UPDATE_HASH"
		),
		runtime: undefined
	}
) {
	const { chunkGraph, runtime } = context;
	hash.update(chunkGraph.getModuleGraphHash(this, runtime));
	if (this.presentationalDependencies !== undefined) {
		for (const dep of this.presentationalDependencies) {
			dep.updateHash(hash, context);
		}
	}
	super.updateHash(hash, context);
}

/**
 * 使模块失效,强制重新构建。
 * 默认实现为空,应由子类覆盖。
 */
invalidateBuild() {
	// should be overridden to support this feature
}

/**
 * 抽象方法:返回模块的唯一标识符。
 * 由子类实现。未实现时抛出异常。
 * @abstract
 */
identifier() {
	const AbstractMethodError = require("./AbstractMethodError");
	throw new AbstractMethodError();
}

/**
 * 抽象方法:返回模块用户可读的标识符(短路径)。
 * 由子类实现。未实现时抛出异常。
 * @abstract
 */
readableIdentifier(requestShortener) {
	const AbstractMethodError = require("./AbstractMethodError");
	throw new AbstractMethodError();
}

/**
 * 抽象方法:构建模块。
 * 由子类实现。未实现时抛出异常。
 * @abstract
 */
build(options, compilation, resolver, fs, callback) {
	const AbstractMethodError = require("./AbstractMethodError");
	throw new AbstractMethodError();
}
相关推荐
海晨忆2 分钟前
【Vue】v-if和v-show的区别
前端·javascript·vue.js·v-show·v-if
1024小神31 分钟前
在GitHub action中使用添加项目中配置文件的值为环境变量
前端·javascript
齐尹秦40 分钟前
CSS 列表样式学习笔记
前端
Mnxj44 分钟前
渐变边框设计
前端
用户7678797737321 小时前
由Umi升级到Next方案
前端·next.js
快乐的小前端1 小时前
TypeScript基础一
前端
北凉温华1 小时前
UniApp项目中的多服务环境配置与跨域代理实现
前端
源柒1 小时前
Vue3与Vite构建高性能记账应用 - LedgerX架构解析
前端
Danny_FD1 小时前
常用 Git 命令详解
前端·github
stanny1 小时前
MCP(上)——function call 是什么
前端·mcp