webpack 在编译代码过程中,会触发⼀系列 Tapable 钩⼦事件,插件所做的,就是找到相应的钩⼦,往上⾯挂上⾃⼰的任务,也就是注册事件,这样当 webpack 构建的时候,插件注册的事件就会随着钩⼦的触发⽽执⾏了。
plugin: 开始打包,在某个时刻,帮助我们处理⼀些什么事情的机制。
plugin 要⽐ loader 稍微复杂⼀些,在 webpack 的源码中,⽤ plugin 的机制还是占有⾮常⼤的场景,可以说plugin 是 webpack 的灵魂。
插件没有像 loader 那样的独立运行环境,只能在 webpack 里面运行
插件的基本结构
class MyPlugin { // 插件名称
apply(compiler) { // 插件上的 apply 方法
compiler.hooks.done.tap(' My Plugin', ( // 插件的 hooks
stats /* stats is passed as argument when done hook is tapped. */
) => {
console.log('Hello World!'); // 插件处理逻辑
});
}
}
module.exports = MyPlugin;
插件使用:
const path = require("path");
const DemoPlugin = require("./plugins/demo-plugin.js");
const PATHS = {
lib: path.join(__dirname, "app", "shake.js"),
build: path.join(__dirname, "build"),
};
module.exports = {
entry: {
lib: PATHS.lib,
},
output: {
path: PATHS.build,
filename: "[name].js",
},
plugins: [new DemoPlugin()],
}
一个最简单的插件
src/demo-plugin.js
module.exports = class DemoPlugin {
constructor(options) {
this.options = options; //获取传递的参数
}
apply() {
console.log("apply", this.options);
}
}
加入到 webpack 配置中
module.exports = {
...
plugins: [new DemoPlugin({ name: "demo" })]
};
插件的错误处理
- 参数校验阶段可以直接 throw 的方式抛出
throw new Error(“ Error Message”)
通过 compilation 对象的 warnings 和 errors 接收
compilation.warnings.push("warning");
compilation.errors.push("error");
通过 Compilation 进行文件写入
Compilation 上的 assets 可以用于文件写入
可以将 zip 资源包设置到 compilation.assets 对象上
文件写入需要使用 webpack-sources (https://www.npmjs.com/package/webpack-sources)
const { RawSource } = require("webpack-sources");
module.exports = class DemoPlugin {
constructor(options) {
this.options = options;
}
apply(compiler) {
const { name } = this.options;
compiler.plugin("emit", (compilation, cb) => {
compilation.assets[name] = new RawSource("demo");
cb();
});
}
};
编写插件的插件
插件自身也可以通过暴露 hooks 的方式进行自身扩展,以 html-webpack-plugin 为例:
- html-webpack-plugin-alter-chunks (Sync)
- html-webpack-plugin-before-html-generation (Async)
- html-webpack-plugin-alter-asset-tags (Async)
- html-webpack-plugin-after-html-processing (Async)
- html-webpack-plugin-after-emit (Async)
编写一个压缩构建资源为zip包的插件
- 生成的 zip 包文件名称可以通过插件传入
- 需要使用 compiler 对象上的特地 hooks 进行资源的生成
Node.js 里面将文件压缩为 zip 包,使用 jszip (https://www.npmjs.com/package/jszip))
var zip = new JSZip();
zip.file("Hello.txt", "Hello World\n");
var img = zip.folder("images");
img.file("smile.gif", imgData, {base64: true});
zip.generateAsync({type:"blob"}).then(function(content) {
// see FileSaver.js
saveAs(content, "example.zip");
});
创建copyright-webpack-plugin.js
class CopyrightWebpackPlugin {
constructor() {
}
//compiler:webpack实例
apply(compiler) {
}
}
module.exports = CopyrightWebpackPlugin;
配置⽂件⾥使⽤
const CopyrightWebpackPlugin = require("./plugin/copyright-webpack-plugin");
plugins: [new CopyrightWebpackPlugin()]
如何传递参数 ```javascript //webpack配置⽂件 plugins: [ new CopyrightWebpackPlugin({ name: ‘开课吧’ }) ];
//copyright-webpack-plugin.js class CopyrightWebpackPlugin { constructor(options) { //接受参数 console.log(options); } apply(compiler) {} } module.exports = CopyrightWebpackPlugin;
- **配置 plugin 在什么时刻进⾏**
```javascript
class CopyrightWebpackPlugin {
constructor(options) {
// console.log(options);
}
apply(compiler) {
//hooks.emit 定义在某个时刻
compiler.hooks.emit.tapAsync(
'CopyrightWebpackPlugin',
(compilation, cb) => {
compilation.assets['copyright.txt'] = {
source: function () {
return 'hello copy';
},
size: function () {
return 20;
}
};
cb();
}
);
//同步的写法
//compiler.hooks.compile.tap("CopyrightWebpackPlugin", compilation => {
// console.log("开始了");
//});
}
}
module.exports = CopyrightWebpackPlugin;
具体有哪些预先定义好的钩子,我们可以参考官方文档的 API: Compiler Hooks:https://webpack.js.org/api/compiler-hooks/ Compilation Hooks:https://webpack.js.org/api/compilation-hooks/ JavascriptParser Hooks:https://webpack.js.org/api/parser/