一、getCompilationHooks(compilation)
静态方法
用于为某个 Compilation
实例附加或获取一组 模块构建过程的钩子 (NormalModuleCompilationHooks
):
-
参数校验 :如果不是
Compilation
实例,抛出类型错误。 -
缓存机制 :利用
WeakMap
存储和获取绑定在compilation
上的钩子。 -
注册钩子:
loader
:同步钩子,用于设置loaderContext
。beforeLoaders
:同步钩子,在加载器设置前调用。beforeParse
、beforeSnapshot
:解析/快照前调用。readResourceForScheme
:适配旧的资源读取方式,为兼容性设计(即将弃用)。readResource
:根据 URL scheme 注册的资源读取钩子。needBuild
:异步钩子,用于判断模块是否需要重新构建。
二、构造函数 constructor({...})
负责初始化一个 NormalModule
实例。接收构建时从 NormalModuleFactory
提供的参数:
模块来源信息(从工厂获得):
request
、userRequest
、rawRequest
:模块请求路径的不同形式。type
:模块类型(如javascript/auto
)。binary
:是否为二进制模块(如 asset/webassembly)。parser
、generator
:用于解析与生成模块代码的实例。resource
:资源路径。matchResource
:匹配用资源路径。loaders
:对应的 loader 列表。resolveOptions
:模块的解析选项。
构建相关的缓存和状态信息:
this._source
:模块源代码。this._sourceSizes
:源代码大小缓存。this._sourceTypes
:源类型缓存。_forceBuild
:是否强制构建。_codeGeneratorData
:代码生成相关的数据缓存。_lastSuccessfulBuildMeta
:记录上一次成功构建的元数据。_isEvaluatingSideEffects
:是否正在评估副作用。
三、核心方法
identifier()
返回模块的唯一标识符(作为缓存键使用):
- 格式:
type|request
或type|request|layer
。
readableIdentifier(requestShortener)
返回模块的可读标识符,通常用于日志和调试信息。
libIdent(options)
生成用于 library export 的标识符,常用于 DllPlugin
或模块缓存。
nameForCondition()
返回模块用于条件匹配的路径(主要是用于配置中 test/resource/include
等字段的匹配判断)。
四、缓存相关方法
updateCacheModule(module)
当模块来自缓存时更新其引用属性(如 loader、parser、request 等),以同步当前实例的状态。
cleanupForCache()
清理当前模块中的引用,释放内存以优化缓存大小。特别保留 sourceTypes
和 size()
缓存数据,确保构建统计正常。
js
class NormalModule extends Module {
/**
* 获取当前 compilation 实例对应的 NormalModule 的 hooks。
* 如果之前未创建过,则初始化一组 Hook 并缓存。
*
* @param {Compilation} compilation 当前编译过程中的 Compilation 实例
* @returns {NormalModuleCompilationHooks} 当前 compilation 的 hook 集合
*/
static getCompilationHooks(compilation) {
if (!(compilation instanceof Compilation)) {
throw new TypeError("The 'compilation' argument must be an instance of Compilation");
}
// 从缓存中尝试获取 hooks
let hooks = compilationHooksMap.get(compilation);
if (hooks === undefined) {
// 首次获取,为该 compilation 创建一套新的 hooks
hooks = {
// 在执行 loader 函数时调用
loader: new SyncHook(["loaderContext", "module"]),
// 在设置 loaders 之前调用,可修改 loader 列表
beforeLoaders: new SyncHook(["loaders", "module", "loaderContext"]),
// 在资源被解析前调用
beforeParse: new SyncHook(["module"]),
// 在创建文件系统快照之前调用(主要用于缓存判断)
beforeSnapshot: new SyncHook(["module"]),
// 为向后兼容保留(将在 webpack 6 中废弃)
readResourceForScheme: new HookMap(scheme => {
const hook = hooks.readResource.for(scheme);
return createFakeHook({
// 将回调包装为使用 resource 和 module 的方式
tap: (options, fn) =>
hook.tap(options, loaderContext =>
fn(loaderContext.resource, loaderContext._module)
),
tapAsync: (options, fn) =>
hook.tapAsync(options, (loaderContext, callback) =>
fn(loaderContext.resource, loaderContext._module, callback)
),
tapPromise: (options, fn) =>
hook.tapPromise(options, loaderContext =>
fn(loaderContext.resource, loaderContext._module)
)
});
}),
// 真正用于读取资源(根据 scheme 分类)
readResource: new HookMap(() => new AsyncSeriesBailHook(["loaderContext"])),
// 判断是否需要重新构建模块
needBuild: new AsyncSeriesBailHook(["module", "context"])
};
// 缓存 hook 集合
compilationHooksMap.set(compilation, hooks);
}
return hooks;
}
/**
* NormalModule 构造函数,表示 Webpack 中最常见的模块类型。
* 用于处理像 JS、CSS 等非内建模块。
*
* @param {NormalModuleCreateData} options 创建模块时传入的数据
*/
constructor({
layer,
type,
request,
userRequest,
rawRequest,
loaders,
resource,
resourceResolveData,
context,
matchResource,
parser,
parserOptions,
generator,
generatorOptions,
resolveOptions
}) {
// 初始化父类 Module
super(type, context || getContext(resource), layer);
// 模块请求信息(来自工厂)
this.request = request; // 带 loader 前缀的请求字符串
this.userRequest = userRequest; // 用户可读的请求路径
this.rawRequest = rawRequest; // 原始请求路径(不含 loader)
this.binary = /^(asset|webassembly)\b/.test(type); // 是否为二进制模块(如 wasm 或 asset)
this.parser = parser; // 模块使用的解析器
this.parserOptions = parserOptions; // 解析器选项
this.generator = generator; // 生成器(生成最终的输出资源)
this.generatorOptions = generatorOptions; // 生成器选项
this.resource = resource; // 资源的绝对路径
this.resourceResolveData = resourceResolveData; // 资源解析过程中的数据
this.matchResource = matchResource; // 指定替换资源的路径(通常用于 loader 特殊场景)
this.loaders = loaders; // 模块使用的 loader 列表
if (resolveOptions !== undefined) {
this.resolveOptions = resolveOptions; // 自定义的解析选项
}
// 构建过程状态
this.error = null; // 构建中出现的错误
this._source = null; // 构建生成的源码
this._sourceSizes = undefined; // 缓存各类型源码大小
this._sourceTypes = undefined; // 模块输出的资源类型(如 javascript, asset)
// 缓存控制与构建优化
this._lastSuccessfulBuildMeta = {}; // 上一次成功构建的元信息
this._forceBuild = true; // 是否强制重新构建
this._isEvaluatingSideEffects = false; // 是否正在评估副作用
this._addedSideEffectsBailout = undefined; // 判断副作用跳过的缓存
this._codeGeneratorData = new Map(); // 存储代码生成相关的缓存数据
}
/**
* 获取模块的唯一标识符(用于模块映射与缓存键)
* @returns {string}
*/
identifier() {
if (this.layer === null) {
// 默认情况下,如果是 JS 模块,直接返回 request
if (this.type === JAVASCRIPT_MODULE_TYPE_AUTO) {
return this.request;
}
return `${this.type}|${this.request}`;
}
return `${this.type}|${this.request}|${this.layer}`;
}
/**
* 获取用户可读的模块标识符(用于日志、错误等输出)
*
* @param {RequestShortener} requestShortener
* @returns {string}
*/
readableIdentifier(requestShortener) {
return requestShortener.shorten(this.userRequest);
}
/**
* 获取模块在 library 构建中的唯一标识符(用于构建 library 结构时的依赖引用)
*
* @param {LibIdentOptions} options
* @returns {string | null}
*/
libIdent(options) {
let ident = contextify(
options.context,
this.userRequest,
options.associatedObjectForCache
);
if (this.layer) ident = `(${this.layer})/${ident}`;
return ident;
}
/**
* 获取可用于 chunk 条件匹配的名称(通常是资源路径,不含 query)
*
* @returns {string | null}
*/
nameForCondition() {
const resource = this.matchResource || this.resource;
const idx = resource.indexOf("?");
if (idx >= 0) return resource.slice(0, idx);
return resource;
}
/**
* 使用缓存模块替换当前模块的内部状态(例如编译过程中重新利用缓存)
*
* @param {Module} module 缓存中已有的模块
*/
updateCacheModule(module) {
super.updateCacheModule(module);
const m = /** @type {NormalModule} */ (module);
this.binary = m.binary;
this.request = m.request;
this.userRequest = m.userRequest;
this.rawRequest = m.rawRequest;
this.parser = m.parser;
this.parserOptions = m.parserOptions;
this.generator = m.generator;
this.generatorOptions = m.generatorOptions;
this.resource = m.resource;
this.resourceResolveData = m.resourceResolveData;
this.context = m.context;
this.matchResource = m.matchResource;
this.loaders = m.loaders;
}
/**
* 清理模块内部无关缓存的数据(以释放内存)
* 通常用于构建后阶段,例如缓存写入前的内存优化
*/
cleanupForCache() {
// 构建过的模块,在清理前需要缓存类型和大小信息供 stats 使用
if (this.buildInfo) {
if (this._sourceTypes === undefined) this.getSourceTypes();
for (const type of this._sourceTypes) {
this.size(type); // 缓存 size
}
}
// 调用父类的清理逻辑
super.cleanupForCache();
// 清除内部无关状态
this.parser = undefined;
this.parserOptions = undefined;
this.generator = undefined;
this.generatorOptions = undefined;
}
}
这段代码展示了 NormalModule
的核心职责:
- 构建相关的状态和属性管理(如 parser、generator、resource 等)。
- 模块唯一性和可读性处理(如 identifier、libIdent)。
- 模块缓存机制支持 (如
updateCacheModule
和cleanupForCache
)。 - 钩子机制支持模块构建生命周期扩展 (
getCompilationHooks
)。