0、基本概念
插件是webpack的支柱功能,极大地强化了webpack的构建能力。
webpack 插件由以下组成:
- 一个 JavaScript 命名函数。
- 在插件函数的 prototype 上定义一个
apply
方法。- 指定一个绑定到 webpack 自身的事件钩子。
- 处理 webpack 内部实例的特定数据。
- 功能完成后调用 webpack 提供的回调。
1、webpack Hooks
一个简单的例子:
function MyExampleWebpackPlugin() {
};
// 在插件函数的 prototype 上定义一个 `apply` 方法。
MyExampleWebpackPlugin.prototype.apply = function(compiler) {
// 指定一个挂载到 webpack 自身的事件钩子。
compiler.plugin('webpacksEventHook', function(compilation, callback) {
console.log("This is an example plugin!!!");
// 功能完成后调用 webpack 提供的回调。
callback();
});
};
以上是编写一个插件的基本格式,关于 webpacksEventHook
可以查看文档 webpack 的 Hooks
列举几种hooks:
entryOption
SyncBailHook, webpack 处理完 entry 配置项后触发afterPlugins
SyncHook, 处理完初始化插件后触发afterResolvers
SyncHook, Resolve 安装完成后触发environment
SyncHook, environment 准备好后触发emit
AsyncSeriesHook, 输出文件到 output 目录之前触发
hook的类型总结(引用一张网上的图片):
2、实际案例
clean-webpack-plugin
插件大部分人应该用过,可以在构建项目时清理文件,下面来看一下 clean-webpack-plugin源码 :
class CleanWebpackPlugin {
// ...省略
apply(compiler: Compiler) {
if (!compiler.options.output || !compiler.options.output.path) {
// eslint-disable-next-line no-console
console.warn(
'clean-webpack-plugin: options.output.path not defined. Plugin disabled...',
);
return;
}
this.outputPath = compiler.options.output.path;
/**
* webpack 4+ comes with a new plugin system.
*
* Check for hooks in-order to support old plugin system
*/
const hooks = compiler.hooks;
if (this.cleanOnceBeforeBuildPatterns.length !== 0) {
if (hooks) {
hooks.emit.tap('clean-webpack-plugin', (compilation) => {
this.handleInitial(compilation);
});
} else {
compiler.plugin('emit', (compilation, callback) => {
try {
this.handleInitial(compilation);
callback();
} catch (error) {
callback(error);
}
});
}
}
// ...省略
}
}
可以看到它使用到了 emit
这个hook, 另外发现 webpack4+ 和 老版本的webpack 编写插件时的一些差异: 4+版本使用 compiler.hooks
老版本使用 compiler.plugin
。
还有一些代码没有在这里展示,总体上删除文件就是调用了del.sync。
3、动手编写一个插件
下面按照webpack官网给的例子,来实现一个插件,作用是在生成打包文件的目录额外生成一个文件,记录所有的打包文件。
test-webpack-plugin.js :
class TestWebpackPlugin {
constructor(options) {
}
apply(compoiler) {
compoiler.hooks.emit.tapAsync("TestWebpackPlugin", (compilation, cb) => {
// 在生成文件中,创建一个头部字符串:
var filelist = "build files:\n\n";
// compilation.assets 存放所有生成的文件信息
for (var filename in compilation.assets) {
filelist += ("- "+ filename + "\n");
}
/* 将这个列表作为一个新的文件资源,插入到 webpack 构建中:
* 文件内容和文件长度需要通过source 和 size来定义
*/
compilation.assets["filelist.md"] = {
// 填充文件内容
source: function() {
return filelist;
},
// 文件长度
size: function() {
return filelist.length;
}
};
cb();
})
}
}
module.exports = TestWebpackPlugin
webpack.config.js:
const path = require("path")
const TestWebpackPlugin = require("./plugins/test-webpack-plugin")
module.exports = {
mode: "development",
entry: {
main: "./src/index.js"
},
output: {
path: path.resolve(__dirname, "dist"),
filename: "[name].[hash].js"
},
plugins: [
new TestWebpackPlugin({ name: "测试参数"})
]
}
打包后dist目录多了一个文件: