webpack插件
一个简单的 plugin
plugin
的实现可以是一个类,使用时传入相关配置来创建一个实例,然后放到配置的 plugins
字段中,而 plugin
实例中最重要的方法是 apply
,该方法在 webpack
compiler
安装插件时会被调用一次,apply
接收 webpack
compiler
对象实例的引用,你可以在 compiler
对象实例上注册各种事件钩子函数,来影响 webpack
的所有构建流程,以便完成更多其他的构建任务。
下边的这个例子,是一个可以创建 webpack
构建文件列表 markdown 的 plugin
,实现上相对简单,但呈现了一个 webpack
plugin
的基本形态。
1 | class FileListPlugin { |
webpack 4.0 版本之前使用的是旧版本的 tapable,和新版本插件API有不少区别,基本注册事件一致,只是注册改变了。
开发和调试 plugin
你要在本地开发和调试 webpack plugin 是很容易的一件事情,你只需要创建一个 js 代码文件,如同上述的例子一样,该文件对外暴露一个类,然后在 webpack 配置文件中引用这个文件的代码,照样运行 webpack 构建查看结果即可。大概的配置方式如下:
1 | // 假设我们上述那个例子的代码是 ./plugins/FileListPlugin 这个文件 |
webpack 中的事件钩子
重点关注的是webpack插件钩子,下面重点说下
先来一波文档充充饥 compiler事件钩子和compilation事件钩子
我们可以看到在事件钩子列表中看到,webpack 中会有相当多的事件钩子,基本覆盖了 webpack 构建流程中的每一个步骤,你可以在这些步骤都注册自己的处理函数,来添加额外的功能,这就是 webpack 提供的 plugin 扩展
1 | this.hooks = { |
上面是webpack事件钩子申明的方式,可以看到有很多类型的事件钩子,从这里你可以看到各个事件钩子函数接收的参数是什么,你还会发现事件钩子会有不同的类型,例如 SyncBailHook,AsyncSeriesHook,SyncHook,接下来我们再介绍一下事件钩子的类型以及我们可以如何更好地利用各种事件钩子的类型来开发我们需要的 plugin。
了解事件钩子类型
上述提到的 webpack compiler 中使用了多种类型的事件钩子,根据其名称就可以区分出是同步还是异步的,对于同步的事件钩子来说,注册事件的方法只有 tap 可用,例如上述的 shouldEmit 应该这样来注册事件函数的
1 | apply(compiler) { |
但如果是异步的事件钩子,那么可以使用 tapPromise 或者 tapAsync 来注册事件函数,tapPromise 要求方法返回 Promise 以便处理异步,而 tapAsync 则是需要用 callback 来返回结果,例如:
1 | compiler.hooks.done.tapPromise('PluginName', (stats) => { |
然而 tapable 这个工具库提供的钩子类型远不止这几种,多样化的钩子类型,主要是为了能够覆盖多种使用场景:
连续地执行注册的事件函数
并行地执行注册的事件函数
一个接一个地执行注册的事件函数,从前边的事件函数获取输入,即瀑布流的方式
异步地执行注册的事件函数在允许时停止执行注册的事件函数,一旦一个方法返回了一个非 undefined 的值,
就跳出执行流
除了同步和异步的区别,我们再参考上述这一些使用场景,以及官方文档的 Plugin API,进一步将事件钩子类型做一个区分。
名称带有 parallel
的,注册的事件函数会并行调用,如:
AsyncParallelHook
、AsyncParallelBailHook
名称带有 bail
的,注册的事件函数会被顺序调用,直至一个处理方法有返回值(ParallelBail 的事件函数则会并行调用,第一个返回值会被使用):
SyncBailHook
、AsyncParallelBailHook
、AsyncSeriesBailHook
名称带有 waterfall
的,每个注册的事件函数,会将上一个方法的返回结果作为输入参数,如:
SyncWaterfallHook
、AsyncSeriesWaterfallHook
通过上面的名称可以看出,有一些类型是可以结合到一起的,如 AsyncParallelBailHook,这样它就具备了更加多样化的特性。
了解了 webpack 中使用的各个事件钩子的类型,才能在开发 plugin 更好地去把握注册事件的输入和输出,同步和异步,来更好地完成我们想要的构建需求。
1 | // plugin 的实现可以是一个类, 使用时传入相关配置来创建一个实例, 然后放到配置的 plugins 字段中, 而 plugin 实例中最重要的方法是 apply, |