摘要
现在项目大部分都使用webpack 进行编译,一方面 webpack 生命力比较旺盛,另一方面 webpack 的生态非常完善。我们90%的场景都能够满足。但有时候也会遇到一些特定的业务场景,比如有时候会遇到需要对编译后的文件进行一些文件注入 等其他特定化的需求。怎么办呢?那就来写一个业务定制化的webpack 插件吧。
官方文档可见: https://webpack.js.org/contribute/writing-a-plugin/#creating-a-plugin
如何编写插件
首先我们了解下一个插件的主要结构
class DtoolPlugin {
// 构造函数,进行参数校验等功能
constructor(args) {}
// 定义插件执行的方法体,主要逻辑都在这里完成,webpack 会自动调用此方法
apply(compiler) {}
}
下面我们来看下 compiler 这个关键对象,compile 提供了 webpack 一系列操作的钩子函数,下面说一说常用的hooks
官方文档可见:https://webpack.js.org/api/compiler-hooks/#watching
下面以真实案例来讲解
apply(compiler) {
const metaParams = this.metaParams;
const tester = {test: this.test};
compiler.hooks.compilation.tap('DtoolPlugin', (compilation) => {
compilation.hooks.optimizeChunkAssets.tapAsync('DtoolPlugin', (chunks, done) => {
wrapChunks(compilation, chunks);
done();
})
});
// 注入文件方法
function wrapChunks(compilation, chunks) {
chunks.forEach(chunk => {
const args = {
hash: compilation.hash,
chunkhash: chunk.hash
};
chunk.files.forEach(fileName => {
if (ModuleFilenameHelpers.matchObject(tester, fileName)) {
const content = 'xxxxx';
// 注入内容
// assets 对象是编译后的对象,以文件名为key , 值为文件内容 、 文件size 等一系列信息, 通过asset 可以对文件名字 , 文件内容做变更
compilation.assets[fileName] = new ConcatSource(
compilation.assets[fileName],
String(content),
);
}
});
});
}
}
hooks 调用的时候有个类型 plugin types 支持 tap , 有些支持异步类型 tapAsync tapPromise
compiler.hooks.run.tapAsync('MyPlugin', (source, target, routesList, callback) => {
console.log('Asynchronously tapping the run hook.');
callback();
});
compiler.hooks.run.tapPromise('MyPlugin', (source, target, routesList) => {
return new Promise(resolve => setTimeout(resolve, 1000)).then(() => {
console.log('Asynchronously tapping the run hook with a delay.');
});
});
compiler.hooks.run.tapPromise('MyPlugin', async (source, target, routesList) => {
await new Promise(resolve => setTimeout(resolve, 1000));
console.log('Asynchronously tapping the run hook with a delay.');
});