插件向第三方开发者展示了webpack引擎的全部潜力。使用分段构建回调,开发人员可以将自己的行为引入webpack构建过程中。构建插件比构建加载器要高级一些,因为你需要了解一些webpack的底层内部结构,以便与它们挂钩。准备阅读一些源代码!
在 Webpack 运行的生命周期中会广播出许多事件,Plugin 可以监听这些事件,在合适的时机通过 Webpack 提供的 API 改变输出结果。
一个webpack的插件由以下几部分组成:
- 一个 JS 命名函数或者类
- 在其原型中定义apply方法
- 指定要进入的事件钩子
- 操作webpack内部实例特定的数据
- 在功能完成后调用webpack提供的回调函数
下面是一个简单的插件实例:
// A JavaScript class.
class MyExampleWebpackPlugin {
// 定义' apply '作为它的原型方法,compiler作为它的参数
apply(compiler) {
// 指定要绑定的事件钩子
compiler.hooks.emit.tapAsync(
'MyExampleWebpackPlugin',
(compilation, callback) => {
console.log('This is an example plugin!');
console.log(
'Here’s the `compilation` object which represents a single build of assets:',
compilation
);
// 使用webpack提供的插件API来构建
compilation.addModule(/* ... */);
callback();
}
);
}
}
在插件开发中最重要的两个资源就是compiler和compilation对象。理解它们的角色是扩展 webpack 引擎重要的第一步。
compiler 对象代表了完整的 webpack 环境配置。这个对象在启动 webpack 时被一次性建立,并配置好所有可操作的设置,包括 options,loader 和 plugin。当在 webpack 环境中应用一个插件时,插件将收到此 compiler 对象的引用。可以使用它来访问 webpack 的主环境。
compilation 对象代表了一次资源版本构建。当运行 webpack 开发环境中间件时,每当检测到一个文件变化,就会创建一个新的 compilation,从而生成一组新的编译资源。一个 compilation 对象表现了当前的模块资源、编译生成资源、变化的文件、以及被跟踪依赖的状态信息。compilation 对象也提供了很多关键时机的回调,以供插件做自定义处理时选择使用。
# 基本插件架构
- 插件是由「具有 apply 方法的 prototype 对象」所实例化出来的
- 这个 apply 方法在安装插件时,会被 webpack compiler 调用一次
- apply 方法可以接收一个 webpack compiler 对象的引用,从而可以在回调函数中访问到 compiler 对象
class HelloWorldPlugin {
apply(compiler) {
compiler.hooks.done.tap('Hello World Plugin', (
stats /* stats is passed as an argument when done hook is tapped. */
) => {
console.log('Hello World!');
});
}
}
module.exports = HelloWorldPlugin;
# 异步事件钩子
# 同步
tap方法
class DonePlugin {
constructor(options) {
this.options = options;
}
apply(compiler) {
compiler.hooks.done.tap("DonePlugin", (stats) => {
console.log("Hello ", this.options.name);
});
}
}
module.exports = DonePlugin;
# 异步
当我们使用tapAsync方法进入插件时,我们需要调用回调函数,它作为函数的最后一个参数提供。
class DonePlugin {
constructor(options) {
this.options = options;
}
apply(compiler) {
compiler.hooks.done.tapAsync("DonePlugin", (stats, callback) => {
console.log("Hello ", this.options.name);
callback();
});
}
}
module.exports = DonePlugin;
当我们使用tapPromise方法访问插件时,我们需要返回一个promise,当异步任务完成时它会解析。
class DonePlugin {
constructor(options) {
this.options = options;
}
apply(compiler) {
compiler.hooks.done.tapPromise("DonePlugin", (stats, callback) => {
return new Promise((resolve, reject) => {
console.log("Hello ", this.options.name);
callback();
});
});
}
}
module.exports = DonePlugin;
# 使用插件
要安装这个插件,只需要在你的 webpack 配置的 plugin 数组中添加一个实例
const DonePlugin = require("./plugins/DonePlugin");
module.exports = {
entry: "./src/index.js",
output: {
path: path.resolve("build"),
filename: "bundle.js",
},
plugins: [new DonePlugin({ name: "zhufeng" })],
};
Webpack 启动后,在读取配置的过程中会先执行 new DonePlugin(options) 初始化一个 DonePlugin 获得其实例。 在初始化 compiler 对象后,再调用 donePlugin.apply(compiler) 给插件实例传入 compiler 对象。 插件实例在获取到 compiler 对象后,就可以通过 compiler.hooks 监听到 Webpack 广播出来的事件。 并且可以通过 compiler 对象去操作 Webpack。
参考: