这段代码是 Webpack 中模块基类 Module
的一部分,提供了模块在构建过程中所需的核心行为定义,供子类(如 NormalModule
)继承和重写。它主要包含源码生成、模块缓存管理、串行化支持等通用接口。
在源码生成方面,getSourceTypes
用于返回当前模块支持的源码类型(例如 JavaScript 类型),而 source
方法是一个已废弃的接口,保留用于兼容旧逻辑,现在推荐使用 codeGeneration
方法来生成模块的源码。codeGeneration
会根据构建上下文返回多个类型的源码及所需的运行时依赖集合。此外,originalSource
提供了模块最初的源文件(未被 Webpack 转换的版本),但默认实现返回 null
,需要子类实现。
模块缓存相关逻辑包括 updateCacheModule
(用于更新已有缓存模块的状态)、getUnsafeCacheData
和 _restoreFromUnsafeCache
(用于支持非安全缓存场景)、cleanupForCache
(释放可回收的引用)。这些方法在构建缓存优化中起到重要作用。
此外,这段代码还提供了模块参与构建判断和依赖收集的相关接口,比如 chunkCondition
判断模块是否可以加入某个 chunk、addCacheDependencies
用于收集构建所依赖的文件或上下文。还有模块序列化与反序列化相关方法 serialize
和 deserialize
,支持模块对象在构建过程中的状态持久化。
整体来看,这些方法为 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);
}