class Order {
constructor() {
this.hooks = { //hooks
goods: new SyncHook(['goodsId', 'number']),
consumer: new AsyncParallelHook(['userId', 'orderId'])
}
}
// 所有的钩子都继承于Hook
class Sync* extends Hook {
tapAsync() { // Sync*类型的钩子不支持tapAsync
throw new Error("tapAsync is not supported on a Sync*");
}
tapPromise() {// Sync*类型的钩子不支持tapPromise
throw new Error("tapPromise is not supported on a Sync*");
}
compile(options) { // 编译代码来按照一定的策略执行Plugin
factory.setup(this, options);
return factory.create(options);
}
}
webpack是现代前端开发中最火的模块打包工具,只需要通过简单的配置,便可以完成模块的加载和打包。那它是怎么做到通过对一些插件的配置,便可以轻松实现对代码的构建呢?
webpack的配置
从上面我萌可以看到,webpack配置中需要理解几个核心的概念
Entry
、Output
、Loaders
、Plugins
、Chunk
Loaders
将各类型的文件处理成webpack能够处理的模块,plugins
有着很强的能力。插件的范围包括,从打包优化和压缩,一直到重新定义环境中的变量。但也是最复杂的一个。比如对js文件进行压缩优化的UglifyJsPlugin
插件CommonsChunkPlugin
将一些公共代码分割成一个chunk,实现单独加载。在webpack4 中CommonsChunkPlugin
被废弃,使用SplitChunksPlugin
webpack详解
读到这里,或许你对webpack有一个大概的了解,那webpack 是怎么运行的呢?我萌都知道,webpack是高度复杂抽象的插件集合,理解webpack的运行机制,对于我萌日常定位构建错误以及写一些插件处理构建任务有很大的帮助。
不得不说的tapable
webpack本质上是一种事件流的机制,它的工作流程就是将各个插件串联起来,而实现这一切的核心就是Tapable,webpack中最核心的负责编译的
Compiler
和负责创建bundles的Compilation
都是Tapable的实例。在Tapable1.0之前,也就是webpack3及其以前使用的Tapable,提供了包括plugin(name:string, handler:function)
注册插件到Tapable对象中apply(…pluginInstances: (AnyPlugin|function)[])
调用插件的定义,将事件监听器注册到Tapable实例注册表中applyPlugins*(name:string, …)
多种策略细致地控制事件的触发,包括applyPluginsAsync
、applyPluginsParallel
等方法实现对事件触发的控制,实现(1)多个事件连续顺序执行 (2)并行执行 (3)异步执行 (4)一个接一个地执行插件,前面的输出是后一个插件的输入的瀑布流执行顺序 (5)在允许时停止执行插件,即某个插件返回了一个
undefined
的值,即退出执行 我们可以看到,Tapable就像nodejs中EventEmitter
,提供对事件的注册on
和触发emit
,理解它很重要,看个栗子:比如我萌来写一个插件在webpack的生命周期中会适时的执行
当然上面提到的Tapable都是1.0版本之前的,如果想深入学习,可以查看Tapable 和 事件流 那1.0的Tapable又是什么样的呢?1.0版本发生了巨大的改变,不再是此前的通过
plugin
注册事件,通过applyPlugins*
触发事件调用,那1.0的Tapable是什么呢?}
对于一个
SyncHook
,我们通过tap
来添加消费者,通过call
来触发钩子的顺序执行。对于一个非
sync*
类型的钩子,即async*
类型的钩子,我萌还可以通过其它方式注册消费者和调用通过上面的栗子,你可能已经大致了解了
Tapable
的用法,它的用法interception
Tapable详解
Sync*
类型的钩子来说。tap
注册,不能使用tapPromise
和tapAsync
注册对于
Async*
类型钩子支持
tap
、tapPromise
、tapAsync
注册}
compile
中调用HookCodeFactory#create
方法编译生成执行代码。在
HookCodeFactory#create
中调用到content
方法,此方法将按照此钩子的执行策略,调用不同的方法来执行编译 生成最终的代码。callTapsSeries
编译生成最终执行插件的函数,callTapsSeries
做的就是将插件列表中插件按照注册顺序遍历执行。undefined
便结束执行列表中的插件callTapsParallel
并行执行插件webpack流程篇
本文关于webpack 的流程讲解是基于webpack4的。
webpack 入口文件
从webpack项目的package.json文件中我萌找到了入口执行函数,在函数中引入webpack,那么入口将会是
lib/webpack.js
,而如果在shell中执行,那么将会走到./bin/webpack.js
,我萌就以lib/webpack.js
为入口开始吧!webpack入口
webpack 的入口文件其实就实例了
Compiler
并调用了run
方法开启了编译,webpack的编译都按照下面的钩子调用顺序执行。编译&构建流程
webpack中负责构建和编译都是
Compilation
在webpack
make
钩子中,tapAsync
注册了一个DllEntryPlugin
, 就是将入口模块通过调用compilation.addEntry
方法将所有的入口模块添加到编译构建队列中,开启编译流程。随后在
addEntry
中调用_addModuleChain
开始编译。在_addModuleChain
首先会生成模块,最后构建。在编译完成后,调用
compilation.seal
方法封闭,生成资源,这些资源保存在compilation.assets
,compilation.chunk
, 在给webpack写插件的时候会用到最后输出
在
seal
执行后,便会调用emit
钩子,根据webpack config文件的output配置的path属性,将文件输出到指定的path.