webpack plugin能够 钩入(hook) 到在每个编译(compilation)中触发的所有关键事件。在编译的每一步,插件都具备完全访问 compiler 对象的能力。使用阶段式的构建回调,开发者可以引入它们自己的行为到 webpack 构建流程中。
官方文档:
- https://webpack.docschina.org/contribute/writing-a-plugin/
- https://webpack.docschina.org/api/plugins/
插件构成:
- 一个 JavaScript class或JavaScript function
- 在它的原型上定义 apply 方法=》将 `apply` 定义为其原型方法,此方法以 compiler 作为参数
- 指定一个触及到 webpack 本身的 事件钩子=》指定要附加到的事件钩子函数
- 操作 webpack 内部的实例特定数据。
- 在实现功能后调用 webpack 提供的 callback。
插件流程说明:插件是由一个构造函数(此构造函数上的 prototype 对象具有 apply 方法)的所实例化出来的。这个 apply 方法在安装插件时,会被 webpack compiler 调用一次。apply 方法可以接收一个 webpack compiler 对象的引用,从而可以在回调函数中访问到 compiler 对象。
compiler 和 compilation(点击链接查看事件钩子分类):
Compiler
模块是 webpack 的主要引擎,它通过 CLI 传递的所有选项, 或者 Node API,创建出一个 compilation 实例。 它扩展(extend)自Tapable
类,用来注册和调用插件。 大多数面向用户的插件会首先在Compiler
上注册。Compilation
模块会被Compiler
用来创建新的 compilation 对象(或新的 build 对象)。compilation
实例能够访问所有的模块和它们的依赖(大部分是循环依赖)。 它会对应用程序的依赖图中所有模块, 进行字面上的编译(literal compilation)。 在编译阶段,模块会被加载(load)、封存(seal)、优化(optimize)、 分块(chunk)、哈希(hash)和重新创建(restore)。
简单案例:插件是一个类,实现在dist文件中打包后生成一个copyright.txt文件
//copyright-webpack-plugin.js:
class CopyrightWebpackPlugin {
constructor() {
console.log("插件被使用了");
}
apply(compiler) {}
}
module.exports = CopyrightWebpackPlugin;
//webpack.config.js:
const copyrightWebpackPlugin = require("./plugins/copyright-webpack-plugin");
module.exports = {
...
plugins: [new copyrightWebpackPlugin()]
}
运行结果如下:
传参:plugin的constructor函数会接受一个options参数,即插件传入的对象
plugins: [
new copyrightWebpackPlugin({
name: "camille"
})
]
//copyright-webpack-plugin.js:
constructor(options) {
console.log("-----options-------", options);
}
如下可以看到接收到的参数:
Tapable:tapable 这个小型 library 是 webpack 的一个核心工具,以提供类似的插件接口。webpack 中许多对象扩展自 Tapable 类。这个类暴露 tap, tapAsync 和 tapPromise 方法。Tapable 提供了 hooks 。compiler hooks 分别记录了 Tapable 内在的钩子,指出哪些 tap 方法可用。根据你触发到 tap 事件,插件可能会以不同的方式运行。
tap、tapAsync、tapPromise:apply中接收compiler,之后可以使用某些钩子通过compiler.hooks.某些钩子,之后再通过.tap触及到这些钩子。
- tap(触及) 某些 hooks,同步的,异步钩子需使用tapAsync 方法或 tapPromise 方法。
- tapAsync 方法 tap 插件时,需要调用 callback,此 callback 将作为最后一个参数传入函数。
- tapPromise 方法 tap 插件时,需要返回一个 promise,此 promise 将在异步任务完成时 resolve。
//copyright-webpack-plugin.js:
class CopyrightWebpackPlugin {
apply(compiler) { //compiler:webpack的实例
//compiler.hooks钩子,类似于react中的生命周期函数
//一个新的编译(compilation)创建之后,钩入(hook into) compiler,同步钩子
compiler.hooks.compile.tap("CopyrightWebpackPlugin", compilation => {
console.log("-----compile assets-------", compilation.assets);
});
//emit:生成资源到 output 目录之前,异步的
//compilation:存放的只是跟这次打包相关的所有内容,与compiler存在差异,compiler配置的内容,包括打包的所有相关内容,
compiler.hooks.emit.tapAsync('CopyrightWebpackPlugin', (compilation, callback) => {
console.log("-----assets-------", compilation.assets);
callback();
})
}
}
module.exports = CopyrightWebpackPlugin;
class HelloAsyncPlugin {
apply(compiler) {
compiler.hooks.emit.tapPromise('HelloAsyncPlugin', compilation => {
// 返回一个 Promise,在我们的异步任务完成时 resolve……
return new Promise((resolve, reject) => {
setTimeout(function() {
console.log('异步工作完成……');
resolve();
}, 1000);
});
});
}
}
运行结果如下:继上面编写的两个loader处理后,如下顺序
还可以自定义的钩子函数、Reporting Progress:prints progress messages
像compilation中有assets中,那么想知道其内都有哪些内容,在代码里console.log()查看,太麻烦,可以借助node调试工具帮助我们快速查看里的内容
node-nightly:https://webpack.docschina.org/contribute/debugging/#devtools
案例实现:
compiler.hooks.emit.tapAsync('CopyrightWebpackPlugin', (compilation, callback) => {
//输出compilation.assets查看只有main.js
//新添加输出资源:copyright.text=》这种方式只是演示,Compilation.assets will be frozen in future, No more changes should happen to Compilation.assets after sealing the Compilation.
compilation.assets['copyright.text'] = {
source: function() {
return "copyright by Camille!";
},
size: function(){
return 21; //"copyright by Camille!"; 21个字节
}
}
callback();
})