Webpack自定义Plugin核心是通过tapable钩子系统在编译生命周期注入逻辑,必须实现apply方法并在Compiler/Compilation合适钩子上注册回调,理解其执行时机与参数。

Webpack 自定义 Plugin 的核心是通过 tapable 提供的钩子系统,在编译生命周期中注入逻辑,比如修改资源、生成文件、校验代码、清理目录等。它不是靠“写个类就行”,而是要正确声明 apply 方法 + 在合适钩子上注册回调,并理解钩子的执行时机和参数。
Plugin 基础结构:必须实现 apply 方法
所有 Webpack Plugin 都是一个带有 apply 方法的对象(函数或类实例均可),Webpack 在启动时会调用它,并传入 compiler 实例:
class MyPlugin {
apply(compiler) {
// 在这里监听 compiler 或 compilation 的钩子
}
}
注意:apply 只执行一次,且发生在 Webpack 初始化阶段,此时还未开始读取入口、解析模块。
关键钩子分两类:Compiler 和 Compilation
Compiler 钩子全局生效,贯穿整个构建过程(如启动、完成、失败);Compilation 钩子在每次构建(含增量/热更新)中新建一个实例,用于操作模块、资源、chunk 等细节:
立即学习“Java免费学习笔记(深入)”;
在 Java 中初始化和管理阿里云 SDK客户端。包括单例模式、线程安全、endpoint 与 region 配置、VPC 终端节点、同步与异步等。
-
Compiler 钩子示例:
-
run:构建开始前(可异步拦截,比如检查环境变量) -
done:构建完全结束(含成功/失败),适合发通知、上传产物 -
emit:即将把资源写入输出目录前,可增删改compilation.assets
-
-
Compilation 钩子示例:
-
buildModule:模块开始构建时(可读取源码、打日志) -
succeedModule:模块构建成功后(适合分析 AST 或收集依赖) -
optimizeChunks:chunk 优化阶段(可合并、拆分、标记 chunk)
-
在 emit 钩子中修改/注入资源(最常用场景)
想自动生成 version.json 或插入构建时间,应在 compiler.hooks.emit.tapAsync 中操作 compilation.assets:
class VersionPlugin {
apply(compiler) {
compiler.hooks.emit.tapAsync('VersionPlugin', (compilation, callback) => {
const versionData = JSON.stringify({
timestamp: new Date().toISOString(),
hash: compilation.hash,
version: '1.2.0'
}, null, 2);
// 注入虚拟文件(不会真实读磁盘,只参与输出)
compilation.assets['version.json'] = {
source: () => versionData,
size: () => versionData.length
};
callback();
});
}
}
注意:tapAsync 表示钩子支持异步(需调用 callback);若同步逻辑用 tap 即可。
访问模块信息与 AST(需配合 Compilation 钩子)
如果要在打包时分析某个 JS 模块是否用了 eval 或 with,可在 compilation.hooks.succeedModule 中获取模块源码并解析:
compiler.hooks.thisCompilation.tap('AnalyzePlugin', (compilation) => {
compilation.hooks.succeedModule.tap('AnalyzePlugin', (module) => {
if (module.resource && module.resource.endsWith('.js')) {
const source = module.originalSource?.source?.() || '';
if (/eval\s*\(/.test(source)) {
console.warn(`[Analyze] eval found in ${module.resource}`);
}
}
});
});
⚠️ 注意:模块源码可能被 loader 处理过(如 Babel),如需原始内容,优先用 module.originalSource?.source();AST 分析建议用 @babel/parser 或 acorn,避免正则误判。

















