webpack 在编译代码过程中,会触发⼀系列 Tapable 钩⼦事件,插件所做的,就是找到相应的钩⼦,往上⾯挂上⾃⼰的任务,也就是注册事件,这样当 webpack 构建的时候,插件注册的事件就会随着钩⼦的触发⽽执⾏了。
plugin: 开始打包,在某个时刻,帮助我们处理⼀些什么事情的机制。
plugin 要⽐ loader 稍微复杂⼀些,在 webpack 的源码中,⽤ plugin 的机制还是占有⾮常⼤的场景,可以说plugin 是 webpack 的灵魂。
插件没有像 loader 那样的独立运行环境,只能在 webpack 里面运行
插件的基本结构
class MyPlugin { // 插件名称apply(compiler) { // 插件上的 apply 方法compiler.hooks.done.tap(' My Plugin', ( // 插件的 hooksstats /* 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.jssaveAs(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 在什么时刻进⾏**```javascriptclass 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/
