You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
asyncHook.intercept({call: (...arg)=>{console.log("Starting asyncHook");},loop: (...args)=>{console.log('restart looping')},tap: (tapInfo)=>{console.log('new hooks be tapped')}register: (tapInfo)=>{// tapInfo = { type: "promise", name: "PromisePlugin", fn: ... }
console.log(`${tapInfo.name} is doing its job`);returntapInfo;// may return a new tapInfo object}})//Contexthooks.intercept({context: true,tap: (context,tapInfo)=>{// tapInfo = { type: "sync", name: "NoisePlugin", fn: ... }console.log(`${tapInfo.name} is doing it's job`);// `context` starts as an empty object if at least one plugin uses `context: true`.// If no plugins use `context: true`, then `context` is undefined.if(context){// Arbitrary properties can be added to `context`, which plugins can then access.context.hasMuffler=true;}}});hooks.tap({name: "NoisePlugin",context: true},(context,newSpeed)=>{if(context&&context.hasMuffler){console.log("Silence...");}else{console.log("Vroom!");}});
_tap(type,options,fn){if(typeofoptions==="string"){options={name: options.trim()};}elseif(typeofoptions!=="object"||options===null){thrownewError("Invalid tap options");}if(typeofoptions.name!=="string"||options.name===""){thrownewError("Missing name for tap");}if(typeofoptions.context!=="undefined"){deprecateContext();}options=Object.assign({ type, fn },options);options=this._runRegisterInterceptors(options);this._insert(options);}
Tapable
Tapable是webpack最重要的库了,如果你需要阅读源码,需要写一个webpack plugin,那么最好先了解Tapable是什么
Tapable其实就是一个EventEmitter库,但它相比普通的EventEmitter,它还支持异步的Event,这样就可以支持异步插件。
上面是来自Tapable的库导出的函数方法,除了Sync的方法,还有Async。
SyncHook
同步的钩子很简单,调用call会执行执行所有被
tapped
的钩子。同步方法除了普通的钩子还有其他的,比如:
AsyncHook
异步的钩子和同步钩子差异的地方在于有两个不同的关键字眼,
Paralle
和Series
。一个可以并行执行钩子,一个可以按顺序执行钩子。可以看到有两种注入异步钩子的方式,一种需要调用回调参数cb,一种需要返回Promise的形式。
上面的
AsyncParallelHook
以一种并行的方式执行,所以执行顺序跟着回调走.另外一种AsyncSeriesHook
根据注册顺序走,只有前面的钩子执行回调函数才会继续下一个钩子,也就是说// macrotask runed
会被先打印Interception和context
hooks还提供了Interception,可以拦截hooks的行为。Context能做为上下文在Interception中传递
HookMap和MultiHook
Tapable还提供了其他辅助方法
源码解析
Hook
https://github.com/webpack/tapable/blob/master/lib/Hook.js
hooks文件实现了需对对外暴露的方法,比如tap,tapAsync,tapAsync,还有一些处理intercept的方法。
所有的tap都会调用_tap函数。首先处理options,然后调用
_runRegisterInterceptors
和_insert
hooks里的调用都会有一个副本,先说明这是因为暴露给用户的函数是通过new Function生成使用的,所以需要保存生产函数的方法,下次再重新赋值
_runRegisterInterceptors
实现了interceptor.register
的功能,用于返回新的options。insert第一行会运行_resetCompilation去重新赋值,前面提前说明了call
,callAsync
,promise
的方法是生成出来的,所以每次tap的时候都需要重新去生成函数。SyncHook和AsyncParallelHook
https://github.com/webpack/tapable/blob/master/lib/SyncHook.js
https://github.com/webpack/tapable/blob/master/lib/AsyncParallelHook.js
每个hooks的代码都大同小异,它们都extends
HookCodeFactory
,并且返回实例化的Hooks。HookCodeFactory
https://github.com/webpack/tapable/blob/master/lib/HookCodeFactory.js
所有的hooks都扩展于
HookCodeFactory
,当hooks.call执行时,会调用HookCodeFactory.prototype.create方法通过new Function生成调用函数。call会调用compile生成函数并保存起来,只有intercept和tap才会重新去编译。
通过字符拼接和new Function,可以创造出异步或者同步代码,并且还能够插入拦截代码。
结尾
Tapable是webpack比较重要的库,基本大部分重要的模块都有它,并需要依赖它来与其他功能模块通信,来完成webpack构建。理解Tapable能够让你更容易去阅读webpack源码或者你只是想写一个Plugin,比如应该在
compiler
的哪个钩子阶段才能拿到编译产生的compiltion
,应该以怎样的方式注入一个钩子进去修改构建产物的信息等等。这种解耦的方式能够很好帮助webpack扩展功能,但是因为源码实现问题,导致async hook不能被await,会产生callback hell,让阅读变得困难The text was updated successfully, but these errors were encountered: