【webpack系列】4. webpack的plugin插件是如何运行的

1,759 阅读3分钟

我们都知道loader和plugin是webpack两个比较重要的配置项,也是深入了解webpack打包原理的关键。上篇文章介绍过loader的运行时机和原理(juejin.cn/post/700239… ),这篇文章再来分析下插件plugin是如何运行的。

1. 为什么需要插件plugin

plugin主要作用是为webpack提供扩展功能的(例如常见的压缩打包文件内容、自动生成html和清除打包文件等功能),通过插件机制可以让webpack更加实用和强大。

2. plugin本质是什么

我们先来看个plugin配置如下:

plugins: [
      // 自动生成html的插件
      new HtmlWebpackPlugin({
        template: './src/index.html'
      })
    ]

通过配置我们可以看到插件是通过new来创建的,可以看出plugin本质就是一个类(具体包含哪些函数后面会继续展开),并支持对象格式的参数传入。

3. plugin是如何执行的

第一篇文章我们分析了webpack的整体执行过程,再来复习下整个流程,如图:

image.png

plugin主要包括初始化、plugin注册和plugin执行三个阶段。

3.1 初始化

e48b3ee168a6b8394580257f4f06198f.png 可以看到webpack会遍历plugins的配置进行初始化,当plugin不是函数时会调用插件的apply方法。通过初始化我们进一步看出plugin本质上是一个函数或者带有apply函数的类(这里的apply不是JavaScript原生的apply,而是插件的方法),并接收compiler。例如:

class MyPlugin{
    constructor(options) {
     // 用来接收配置的参数
    }
     apply(compiler){
     // 事件监听...
     }
 }
 module.exports = MyPlugin;
这也是我们实现自定义插件的基本结构,发现插件并没有很魔法,自己实现是完全有可能的。

3.2 plugin注册

由于plugin接收了compiler对象,所以它可以通过compiler提供的钩子函数进行注册事件。例如:

 compiler.hooks.阶段.事件类型(name, compilation => {
         //事件处理函数
 })
 // 例如自动生成html的HtmlWebpackPlugin插件
 compiler.hooks.compilation.tap('HtmlWebpackPluginHooks', compilation => {

 });

其中事件名称可以随便定义,不重复就行,事件类型是基于tapable,用于钩子函数监听的库,有3种类型:

tap:注册同步的钩子函数,函数运行完毕则表示事件处理结束
tapAsync:注册基于回调的异步的钩子函数,函数通过调用一个回调表示事件处理结束
tapPromise:注册基于Promise的异步的钩子函数,函数通过返回的Promise进入已决状态表示事件处理结束

可能会疑问为什么可以这样使用,因为compiler类的hooks是tapable的实例,所以可以使用tapable的方法。

3.3 plugin执行

plugin的执行是通过call(同步)或callAsync(异步)来触发plugin的执行,例如:compiler文件中的this.hooks.compilation.call(compilation, params)语句就会触发compiler.hooks.compilation.tap注册事件的执行。

知道了这一点也是帮助追踪webpack整体流程非常重要的一点,通过call/callAsync全局搜索找到注册事件,从而串起整个执行过程。

4. 总结

通过上面的分析,相信对于插件相关内容有了进一步了解,所以只有弄清楚原理才能更好的理解框架,才能帮助我们实现个性化的需求。

感谢阅读,别忘了点赞支持哟~

附上历史文章:

【webpack系列】 从源码角度分析webpack打包产出及核心流程

【webpack系列】 从源码角度分析loader是如何触发和执行的

【webpack系列】webpack是如何解析模块的