webpack 模块 第 五 节

这段代码是 Webpack 中模块基类 Module 的一部分,提供了模块在构建过程中所需的核心行为定义,供子类(如 NormalModule)继承和重写。它主要包含源码生成、模块缓存管理、串行化支持等通用接口。

在源码生成方面,getSourceTypes 用于返回当前模块支持的源码类型(例如 JavaScript 类型),而 source 方法是一个已废弃的接口,保留用于兼容旧逻辑,现在推荐使用 codeGeneration 方法来生成模块的源码。codeGeneration 会根据构建上下文返回多个类型的源码及所需的运行时依赖集合。此外,originalSource 提供了模块最初的源文件(未被 Webpack 转换的版本),但默认实现返回 null,需要子类实现。

模块缓存相关逻辑包括 updateCacheModule(用于更新已有缓存模块的状态)、getUnsafeCacheData_restoreFromUnsafeCache(用于支持非安全缓存场景)、cleanupForCache(释放可回收的引用)。这些方法在构建缓存优化中起到重要作用。

此外,这段代码还提供了模块参与构建判断和依赖收集的相关接口,比如 chunkCondition 判断模块是否可以加入某个 chunk、addCacheDependencies 用于收集构建所依赖的文件或上下文。还有模块序列化与反序列化相关方法 serializedeserialize,支持模块对象在构建过程中的状态持久化。

整体来看,这些方法为 Webpack 模块生命周期中的多个阶段提供了默认行为定义,而具体模块(如 JavaScript 模块、CSS 模块)则通过继承和重写这些方法,实现自身的构建逻辑。

js 复制代码
/**
 * 获取模块支持的源码类型
 * 如果没有重写 `source` 方法,则默认返回 'unknown' 类型
 * @abstract
 * @returns {SourceTypes} 可用的源码类型(不要修改)
 */
getSourceTypes() {
	if (this.source === Module.prototype.source) {
		return DEFAULT_TYPES_UNKNOWN;
	}
	return JS_TYPES;
}

/**
 * 已废弃的源码生成方法,推荐使用 codeGeneration 替代
 * 兼容旧逻辑:从 codeGeneration 中获取指定类型源码
 * @deprecated 请使用 codeGeneration()
 * @param {DependencyTemplates} dependencyTemplates 依赖模板
 * @param {RuntimeTemplate} runtimeTemplate 运行时代码模板
 * @param {string=} type 要生成的源码类型,默认 'javascript'
 * @returns {Source} 生成的源码
 */
source(dependencyTemplates, runtimeTemplate, type = "javascript") {
	if (this.codeGeneration === Module.prototype.codeGeneration) {
		const AbstractMethodError = require("./AbstractMethodError");
		throw new AbstractMethodError();
	}
	const chunkGraph = ChunkGraph.getChunkGraphForModule(
		this,
		"Module.source() is deprecated. Use Compilation.codeGenerationResults.getSource(module, runtime, type) instead",
		"DEP_WEBPACK_MODULE_SOURCE"
	);
	/** @type {CodeGenerationContext} */
	const codeGenContext = {
		dependencyTemplates,
		runtimeTemplate,
		moduleGraph: chunkGraph.moduleGraph,
		chunkGraph,
		runtime: undefined,
		codeGenerationResults: undefined
	};
	const sources = this.codeGeneration(codeGenContext).sources;

	return /** @type {Source} */ (
		type
			? sources.get(type)
			: sources.get(/** @type {string} */ (first(this.getSourceTypes())))
	);
}

/**
 * 获取模块估算体积,需重写
 * @abstract
 * @param {string=} type 要估算的源码类型
 * @returns {number} 模块大小(必须非零)
 */
size(type) {
	const AbstractMethodError = require("./AbstractMethodError");
	throw new AbstractMethodError();
}

/**
 * 获取用于 library 构建的模块标识符
 * 默认返回 null,可重写
 * @param {LibIdentOptions} options 参数选项
 * @returns {string | null}
 */
libIdent(options) {
	return null;
}

/**
 * 返回用于条件匹配的模块路径
 * 一般返回 resource,默认返回 null
 * @returns {string | null}
 */
nameForCondition() {
	return null;
}

/**
 * 判断模块为何不能进行 Module Concatenation
 * 默认返回当前类名说明
 * @param {ConcatenationBailoutReasonContext} context 上下文
 * @returns {string | undefined}
 */
getConcatenationBailoutReason(context) {
	return `Module Concatenation is not implemented for ${this.constructor.name}`;
}

/**
 * 判断模块是否有副作用(用于 Tree Shaking)
 * 默认返回 true,即假设有副作用
 * @param {ModuleGraph} moduleGraph 模块图
 * @returns {ConnectionState}
 */
getSideEffectsConnectionState(moduleGraph) {
	return true;
}

/**
 * 推荐的源码生成方法,生成所有类型的源码
 * 可包含运行时依赖集等信息
 * @param {CodeGenerationContext} context 上下文
 * @returns {CodeGenerationResult}
 */
codeGeneration(context) {
	const sources = new Map();
	for (const type of this.getSourceTypes()) {
		if (type !== "unknown") {
			sources.set(
				type,
				this.source(
					context.dependencyTemplates,
					context.runtimeTemplate,
					type
				)
			);
		}
	}
	return {
		sources,
		runtimeRequirements: new Set([
			RuntimeGlobals.module,
			RuntimeGlobals.exports,
			RuntimeGlobals.require
		])
	};
}

/**
 * 判断模块是否可以包含在指定 chunk 中
 * 默认返回 true
 * @param {Chunk} chunk chunk 对象
 * @param {Compilation} compilation 编译对象
 * @returns {boolean}
 */
chunkCondition(chunk, compilation) {
	return true;
}

/**
 * 判断是否重写了 chunkCondition 方法
 * @returns {boolean}
 */
hasChunkCondition() {
	return this.chunkCondition !== Module.prototype.chunkCondition;
}

/**
 * 使用新模块更新当前缓存模块的内部字段
 * @param {Module} module 新模块对象
 * @returns {void}
 */
updateCacheModule(module) {
	this.type = module.type;
	this.layer = module.layer;
	this.context = module.context;
	this.factoryMeta = module.factoryMeta;
	this.resolveOptions = module.resolveOptions;
}

/**
 * 获取当前模块可用于不安全缓存的字段
 * @returns {UnsafeCacheData}
 */
getUnsafeCacheData() {
	return {
		factoryMeta: this.factoryMeta,
		resolveOptions: this.resolveOptions
	};
}

/**
 * 从不安全缓存中恢复字段
 * @param {object} unsafeCacheData 缓存数据
 * @param {NormalModuleFactory} normalModuleFactory 模块工厂
 */
_restoreFromUnsafeCache(unsafeCacheData, normalModuleFactory) {
	this.factoryMeta = unsafeCacheData.factoryMeta;
	this.resolveOptions = unsafeCacheData.resolveOptions;
}

/**
 * 清理可释放的字段引用以节省内存
 */
cleanupForCache() {
	this.factoryMeta = undefined;
	this.resolveOptions = undefined;
}

/**
 * 返回模块在 webpack 转换前的原始源码
 * 默认返回 null
 * @returns {Source | null}
 */
originalSource() {
	return null;
}

/**
 * 向不同依赖集合中添加构建缓存依赖
 * 子类可重写
 * @param {LazySet<string>} fileDependencies 文件依赖
 * @param {LazySet<string>} contextDependencies 上下文依赖
 * @param {LazySet<string>} missingDependencies 缺失依赖
 * @param {LazySet<string>} buildDependencies 构建依赖
 */
addCacheDependencies(
	fileDependencies,
	contextDependencies,
	missingDependencies,
	buildDependencies
) {}

/**
 * 序列化模块,用于缓存
 * @param {ObjectSerializerContext} context 上下文
 */
serialize(context) {
	const { write } = context;
	write(this.type);
	write(this.layer);
	write(this.context);
	write(this.resolveOptions);
	write(this.factoryMeta);
	write(this.useSourceMap);
	write(this.useSimpleSourceMap);
	write(this.hot);
	write(
		this._warnings !== undefined && this._warnings.length === 0
			? undefined
			: this._warnings
	);
	write(
		this._errors !== undefined && this._errors.length === 0
			? undefined
			: this._errors
	);
	write(this.buildMeta);
	write(this.buildInfo);
	write(this.presentationalDependencies);
	write(this.codeGenerationDependencies);
	super.serialize(context);
}

/**
 * 反序列化模块,恢复字段
 * @param {ObjectDeserializerContext} context 上下文
 */
deserialize(context) {
	const { read } = context;
	this.type = read();
	this.layer = read();
	this.context = read();
	this.resolveOptions = read();
	this.factoryMeta = read();
	this.useSourceMap = read();
	this.useSimpleSourceMap = read();
	this.hot = read();
	this._warnings = read();
	this._errors = read();
	this.buildMeta = read();
	this.buildInfo = read();
	this.presentationalDependencies = read();
	this.codeGenerationDependencies = read();
	super.deserialize(context);
}
相关推荐
海晨忆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