我们都知道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的整体执行过程,再来复习下整个流程,如图:
plugin主要包括初始化、plugin注册和plugin执行三个阶段。
3.1 初始化
可以看到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是如何解析模块的