插件向第三方开发者提供了 webpack 引擎中完整的能力。使用阶段式的构建回调,开发者能够引入它们本身的行为到 webpack 构建流程中。建立插件比建立 loader 更加高级,由于你将须要理解一些 webpack 底层的内部特性来作相应的钩子,因此作好阅读一些源码的准备!javascript
webpack
插件由如下组成:java
apply
方法。// 一个 JavaScript 命名函数。 function MyExampleWebpackPlugin() { }; // 在插件函数的 prototype 上定义一个 `apply` 方法。 MyExampleWebpackPlugin.prototype.apply = function(compiler) { // 指定一个挂载到 webpack 自身的事件钩子。 compiler.plugin('webpacksEventHook', function(compilation /* 处理 webpack 内部实例的特定数据。*/, callback) { console.log("This is an example plugin!!!"); // 功能完成后调用 webpack 提供的回调。 callback(); }); };
在插件开发中最重要的两个资源就是 compiler
和 compilation
对象。理解它们的角色是扩展 webpack 引擎重要的第一步。webpack
compiler
对象表明了完整的 webpack 环境配置。这个对象在启动 webpack 时被一次性创建,并配置好全部可操做的设置,包括 options,loader 和 plugin。当在 webpack 环境中应用一个插件时,插件将收到此 compiler 对象的引用。可使用它来访问 webpack 的主环境。git
compilation
对象表明了一次资源版本构建。当运行 webpack 开发环境中间件时,每当检测到一个文件变化,就会建立一个新的 compilation,从而生成一组新的编译资源。一个 compilation 对象表现了当前的模块资源、编译生成资源、变化的文件、以及被跟踪依赖的状态信息。compilation 对象也提供了不少关键时机的回调,以供插件作自定义处理时选择使用。github
这两个组件是任何 webpack 插件不可或缺的部分(特别是 compilation
),所以,开发者在阅读源码,并熟悉它们以后,会感到获益匪浅:web
插件是由「具备 apply
方法的 prototype 对象」所实例化出来的。这个 apply
方法在安装插件时,会被 webpack compiler 调用一次。apply
方法能够接收一个 webpack compiler 对象的引用,从而能够在回调函数中访问到 compiler 对象。一个简单的插件结构以下:api
function HelloWorldPlugin(options) { // 使用 options 设置插件实例…… } HelloWorldPlugin.prototype.apply = function(compiler) { compiler.plugin('done', function() { console.log('Hello World!'); }); }; module.exports = HelloWorldPlugin;
而后,要安装这个插件,只须要在你的 webpack 配置的 plugin
数组中添加一个实例:数组
var HelloWorldPlugin = require('hello-world'); var webpackConfig = { // ... 这里是其余配置 ... plugins: [ new HelloWorldPlugin({options: true}) ] };
使用 compiler 对象时,你能够绑定提供了编译 compilation 引用的回调函数,而后拿到每次新的 compilation 对象。这些 compilation 对象提供了一些钩子函数,来钩入到构建流程的不少步骤中。架构
function HelloCompilationPlugin(options) {} HelloCompilationPlugin.prototype.apply = function(compiler) { // 设置回调来访问 compilation 对象: compiler.plugin("compilation", function(compilation) { // 如今,设置回调来访问 compilation 中的步骤: compilation.plugin("optimize", function() { console.log("Assets are being optimized."); }); }); }; module.exports = HelloCompilationPlugin;
关于 compiler
, compilation
的可用回调,和其它重要的对象的更多信息,请查看 插件 文档。app
有一些编译插件中的步骤是异步的,这样就须要额外传入一个 callback 回调函数,而且在插件运行结束时,_必须_调用这个回调函数。
function HelloAsyncPlugin(options) {} HelloAsyncPlugin.prototype.apply = function(compiler) { compiler.plugin("emit", function(compilation, callback) { // 作一些异步处理…… setTimeout(function() { console.log("Done with async work..."); callback(); }, 1000); }); }; module.exports = HelloAsyncPlugin;
一旦能咱们深刻理解 webpack compiler 和每一个独立的 compilation,咱们依赖 webpack 引擎将有无限多的事能够作。咱们能够从新格式化已有的文件,建立衍生的文件,或者制做全新的生成文件。
让咱们来写一个简单的示例插件,生成一个叫作 filelist.md
的新文件;文件内容是全部构建生成的文件的列表。这个插件大概像下面这样:
function FileListPlugin(options) {} FileListPlugin.prototype.apply = function(compiler) { compiler.plugin('emit', function(compilation, callback) { // 在生成文件中,建立一个头部字符串: var filelist = 'In this build:\n\n'; // 遍历全部编译过的资源文件, // 对于每一个文件名称,都添加一行内容。 for (var filename in compilation.assets) { filelist += ('- '+ filename +'\n'); } // 将这个列表做为一个新的文件资源,插入到 webpack 构建中: compilation.assets['filelist.md'] = { source: function() { return filelist; }, size: function() { return filelist.length; } }; callback(); }); }; module.exports = FileListPlugin;
webpack 插件能够按照它所注册的事件分红不一样的类型。每个事件钩子决定了它该如何应用插件的注册。
applyPlugins(name: string, args: any...)
applyPluginsBailResult(name: string, args: any...)
这意味着每一个插件回调,都会被特定的 args
一个接一个地调用。 这是插件的最基本形式。许多有用的事件(例如 "compile"
, "this-compilation"
),预期插件会同步执行。
applyPluginsWaterfall(name: string, init: any, args: any...)
这种类型,每一个插件都在其余插件依次调用以后调用,前一个插件调用的返回值,做为参数传入后一个插件。这类插件必须考虑其执行顺序。 必须等前一个插件执行后,才能接收参数。第一个插件的值是初始值(init)
。这个模式用在与 webpack
模板相关的 Tapable 实例中(例如 ModuleTemplate
, ChunkTemplate
等)。
applyPluginsAsync(name: string, args: any..., callback: (err?: Error) -> void)
这种类型,插件处理函数在调用时,会传入全部的参数和一个签名为 (err?: Error) -> void
的回调函数。处理函数按注册时的顺序调用。在调用完全部处理程序后,才会调用 callback
。 这也是 "emit"
, "run"
等事件的经常使用模式。
applyPluginsAsyncWaterfall(name: string, init: any, callback: (err: Error, result: any) -> void)
这种类型,插件处理函数在调用时,会传入当前值(current value)和一个带有签名为 (err: Error, nextValue: any) -> void.
的回调函数。当调用的 nextValue
是下一个处理函数的当前值(current value)时,第一个处理程序的当前值是 init
。在调用完全部处理函数以后,才会调用 callback,并将最后一个值传入。若是其中任何一个处理函数传入一个 err
值,则会调用此 callback 并将此 error 对象传入,而且再也不调用其余处理函数。 这种插件模式适用于像 "before-resolve"
和 "after-resolve"
这样的事件。
applyPluginsAsyncSeries(name: string, args: any..., callback: (err: Error, result: any) -> void)
-并行(parallel) -
applyPluginsParallel(name: string, args: any..., callback: (err?: Error) -> void)
applyPluginsParallelBailResult(name: string, args: any..., callback: (err: Error, result: any) -> void)