通过插件我们可以扩展webpack,使webpack可以执行更广泛的任务,拥有更强的构建能力。
webpack 就像一条生产线,要经过一系列处理流程后才能将源文件转换成输出结果。 这条生产线上的每个处理流程的职责都是单一的,多个流程之间有存在依赖关系,只有完成当前处理后才能交给下一个流程去处理。 插件就像是一个插入到生产线中的一个功能,在特定的时机对生产线上的资源做处理。webpack 通过 Tapable 来组织这条复杂的生产线。 webpack 在运行过程中会广播事件,插件只需要监听它所关心的事件,就能加入到这条生产线中,去改变生产线的运作。 webpack 的事件流机制保证了插件的有序性,使得整个系统扩展性很好。 ——「深入浅出 Webpack」
站在代码逻辑的角度就是:webpack在编译过程中,会触发一些列 Tapable钩子事件,插件所做的就是找到相应的钩子,往上面挂载自己的任务,也就是注册事件,这样,当webpack执行到相关的钩子函数时就会触发我们挂载的任务事件。
Tapable 为 webpack 提供了统一的插件接口(钩子)类型定义,它是 webpack 的核心功能库。webpack 中目前有十种 hooks,在 Tapable 源码中可以看到,他们是:
// https://github.com/webpack/tapable/blob/master/lib/index.js
exports.SyncHook = require("./SyncHook");
exports.SyncBailHook = require("./SyncBailHook");
exports.SyncWaterfallHook = require("./SyncWaterfallHook");
exports.SyncLoopHook = require("./SyncLoopHook");
exports.AsyncParallelHook = require("./AsyncParallelHook");
exports.AsyncParallelBailHook = require("./AsyncParallelBailHook");
exports.AsyncSeriesHook = require("./AsyncSeriesHook");
exports.AsyncSeriesBailHook = require("./AsyncSeriesBailHook");
exports.AsyncSeriesLoopHook = require("./AsyncSeriesLoopHook");
exports.AsyncSeriesWaterfallHook = require("./AsyncSeriesWaterfallHook");
exports.HookMap = require("./HookMap");
exports.MultiHook = require("./MultiHook");
Tapable 还统一暴露了三个方法给插件,用于注入不同类型的自定义构建行为:
compiler 对象中保存着完整的 Webpack 环境配置,每次启动 webpack 构建时它都是一个独一无二,仅仅会创建一次的对象。
这个对象会在首次启动 Webpack 时创建,我们可以通过 compiler 对象上访问到 Webapck 的主环境配置,比如 loader 、 plugin 等等配置信息。
它有以下主要属性:
compilation 对象代表一次资源的构建,compilation 实例能够访问所有的模块和它们的依赖。
一个 compilation 对象会对构建依赖图中所有模块,进行编译。 在编译阶段,模块会被加载(load)、封存(seal)、优化(optimize)、 分块(chunk)、哈希(hash)和重新创建(restore)。
它有以下主要属性:
class TestPlugin {
constructor() {
console.log("TestPlugin constructor()");
}
// 1. webpack读取配置时,new TestPlugin() ,会执行插件 constructor 方法
// 2. webpack创建 compiler 对象
// 3. 遍历所有插件,调用插件的 apply 方法
apply(compiler) {
console.log("TestPlugin apply()");
}
}
module.exports = TestPlugin;
// plugins/analyze-webpack-plugin.js
class AnalyzeWebpackPlugin {
apply(compiler) {
// emit是异步串行钩子
compiler.hooks.emit.tap("AnalyzeWebpackPlugin", (compilation) => {
// Object.entries将对象变成二维数组。二维数组中第一项值是key,第二项值是value
const assets = Object.entries(compilation.assets);
let source = "# 分析打包资源大小 \n| 名称 | 大小 |\n| --- | --- |";
assets.forEach(([filename, file]) => {
source += `\n| ${filename} | ${file.size()} |`;
});
// 添加资源
compilation.assets["analyze.md"] = {
source() {
return source;
},
size() {
return source.length;
},
};
});
}
}
module.exports = AnalyzeWebpackPlugin;
在webpack.config.js中配置
const AnalyzeWebpackPlugin = require("./plugins/analyze-webpack-plugin");
//...省略
plugins: [
new HtmlWebpackPlugin({
template: path.resolve(__dirname, 'public/index.html')
}),
new AnalyzeWebpackPlugin()
],