写自己的插件(一):注册钩子并在 emit 阶段生成文件
本节目标
- 写出一个最小可运行的 Webpack 插件类
- 在
emit(或 processAssets)阶段往产物里加一个新文件 - 理解
compilation.assets就是「最终输出清单」
现在动手。一个 Webpack 插件,就是一个含有 apply(compiler) 方法的类(或对象)。我们在「即将输出文件」的钩子上,往 compilation.assets 里追加一个文件,就完成了「生成文件」。
下面这个插件会在每次构建后,自动生成一个 build-info.txt,记录构建时间与入口数量:
// my-first-plugin.js
class BuildInfoPlugin {
apply(compiler) {
// 推荐用 thisCompilation + processAssets(webpack5 官方推荐写法)
compiler.hooks.thisCompilation.tap('BuildInfoPlugin', (compilation) => {
compilation.hooks.processAssets.tap(
{
name: 'BuildInfoPlugin',
// 声明本插件在「优化资产之后、 Seal 之前」执行
stage: compilation.constructor.PROCESS_ASSETS_STAGE_REPORT,
},
() => {
const info = [
'构建时间: ' + new Date().toLocaleString(),
'产物文件数: ' + Object.keys(compilation.assets).length,
].join('\n')
// 往最终输出清单里加一个文件
compilation.assets['build-info.txt'] = {
source: () => info, // 文件内容
size: () => info.length, // 文件大小(字节)
}
}
)
})
}
}
module.exports = BuildInfoPlugin在配置里用起来:
// webpack.config.js
const BuildInfoPlugin = require('./my-first-plugin.js')
module.exports = {
entry: './src/index.js',
output: { filename: 'bundle.js', path: __dirname + '/dist', clean: true },
plugins: [new BuildInfoPlugin()],
mode: 'development',
}跑 npx webpack,dist/ 里除了 bundle.js 还会多一个 build-info.txt。关键点:compilation.assets 就是「最终要写到磁盘的资源清单」,你往里塞一个 { source, size } 对象,它就会被输出成文件。就这么简单。
兼容性说明:旧教程常写
compiler.hooks.emit.tap(...)直接改compilation.assets,webpack4/5 都能跑;但 webpack5 官方更推荐processAssets(在资源已生成、尚未封存时操作,更安全可控)。本例用processAssets,是「现代正确姿势」。
名词解释
apply(compiler):插件必须实现的方法,Webpack 在初始化插件时调用它,把 compiler 传进来,你在这里挂钩子。
processAssets:compilation 上的钩子,时机是「所有资产已生成、准备封存输出前」,是往产物里加/改文件的最佳干预点。
compilation.assets:本次构建的「最终资源清单」对象,key 是文件名,value 是带 source()/size() 的资源对象。改它就是改产物。
PROCESS_ASSETS_STAGE_*:webpack5 给 processAssets 划分的执行阶段常量(REPORT / OPTIMIZE / SUMMARIZE 等),用来控制插件间的先后。
课后练习
练习 1:为什么「往 compilation.assets 加对象」就能生成文件,而不是直接写磁盘?
答案:Webpack 是「先收集资产、最后统一写盘」的模型。
assets是输出前的「清单」,Webpack 写完所有钩子后,才按这份清单把每个资源的source()写到磁盘。你改清单而非直接 IO,既统一又可控(还能被后续插件再加工)。
练习 2:emit 钩子和 processAssets 都能加文件,选哪个?
答案:优先
processAssets(webpack5 官方推荐,处在「资产已就绪」阶段,语义清晰、可声明 stage 控制顺序);emit也可用且更「老派」,但它在更靠后的时机,部分资产细节已定型,灵活度略低。
总结
你的第一个插件已经能「自动生成文件」了。核心套路三步走:① 写个带 apply(compiler) 的类;② 在 processAssets 钩子上 .tap;③ 往 compilation.assets 塞 { source, size }。记住 assets 即「输出清单」这个心智模型,后面所有「加文件/改文件」的魔法都源于此。下一节把它升级成两个实战插件:注入版权 banner、生成依赖清单。