6

Under the hood webpack: core library behind the event-driven architecture | code...

 3 years ago
source link: https://codecrumbs.io/stories/webpack-tapable-core/
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

Under the hood webpack: core library behind the event-driven architecture

Quick tip: click code mentions in the text (e.g. file-name.js) or visual elements (e.g. , 4) to highlight them on scheme above.

TL;DR

Webpack architecture is heavily based on events. Each webpack plugin is basically a set of listeners hooked on different events during compilation phases. Under the hood, webpack uses a library called tapable to encapsulate "publish-subscribe" implementation.

Tapable provides different "hooks" classes (SyncBailHook, AsyncParallelHook, etc.) to "hook" on events with some extra rich functionality (e.g. interceptions or cross-listeners integration).

For example, DefinePugin (used to define environment variables, e.g. NODE_ENV) and SizeLimitsPlugin (reports oversized chunks, e.g. size > 250kb) tap into compiler instance hooks: the first one listens to compilation event in order to insert extra variables and the latter to afterEmit event - to proceed with assets evaluation once they were emitted.

DefinePlugin
Compiler
SizeLimitsPlugin
hooks.compilation.tap
hooks.compilation.call
hooks.afterEmit.tap
hooks.afterEmit.callAsync
DefinePlugin
Compiler
SizeLimitsPlugin

Let's have a quick look under the hood of webpack at SizeLimitsPlugin integration.

..lib..performancelib/WebpackOptionsApply.jsWebpackOptionsApply.jslib/performance/SizeLimitsPlugin.jsSizeLimitsPlugin.js
/lib/WebpackOptionsApply.js
/lib/performance/SizeLimitsPlugin.js
0
1
0SizeLimitsPlugin is instantiated and assigned to compiler in WebpackOptionsApply if performance option is enabled from webpack config (highlight )
1Then SizeLimitsPlugin taps on afterEmit event and will sit still until most of the compilation flow is done and that particular event is called.
..lib..performancelib/Compiler.jsCompiler.jslib/performance/SizeLimitsPlugin.jsSizeLimitsPlugin.js
/lib/performance/SizeLimitsPlugin.js
/lib/Compiler.js
Using a simple "pub-sub" mechanism, performance evaluation plugin `SizeLimitsPlugin` can be loosely hooked into compilation flow.
2
3
1
0
`Compiler` calls (0)
 asynchronously `afterEmit` event (providing "compilation" instance).
`SizeLimitsPlugin` subscribes (1) to that to evaluate oversized chunks.

Once the event is called the plugin will do its job (collecting oversized chunks in our case 2, 3).

Indeed, all plugins are assigned similarly, so then you have a pipeline of plugins performing different operations but staying very loosely coupled.

Slightly longer read

Let's learn more about tapable library.

Have a look at this classic events-based architecture all of us have used many times.

..liblib/webpack.jswebpack.js
/lib/webpack.js
All registered subscribers will be notified once particular event is published
1
0

Usually, Pub-Sub (Observer or Listener) behavioral pattern is used to establish a one-to-many relationship: publisher dispatches events 0 and multiple subscribers can listen to them accordingly 1.

Tapable is the Pub-Sub implementation on steroids. One key difference - instead of just string event names, uses the "Hook" class instance per event. It implements tap/tapAsync with call/callAsync methods to listen and publish events, or even intercept them.

..liblib/Compiler.jsCompiler.js
/lib/Compiler.js
2
0
1

On top of classic "Sync" (0) events handling, it also adds "AsyncSeries" (1), "AsyncParallel" (2) (handlers called in a row or parallel, respectively).

On the scheme below you can see another example where plugins use a few more advanced hooks sub-classes from tapable ("Waterfall" and "Bail").

..lib..optimizelib/Compilation.jsCompilation.jslib/FlagDependencyUsagePlugin.jsFlagDependencyUsagePlugin.jslib/optimize/SideEffectsFlagPlugin.jsSideEffectsFlagPlugin.js
/lib/FlagDependencyUsagePlugin.js
/lib/optimize/SideEffectsFlagPlugin.js
/lib/Compilation.js
`FlagDependencyUsagePlugin` optimize chunks by identifying unused dependencis.
1
`SideEffectsFlagPlugin` handling specified side-effects from webpack options.
2
`optimizeDependencies` is an instance of `SyncBailHook`.
0

Using them with callback allows us to pass data between different subscribers (Waterfall) or exit early (Bail).

Event-driven architecture has some pros&cons. Like any other architectures there is no silver bullet. Within webpack, however, it brings endless scalability and works very well with a pipeline of loosely-coupled plugins.

To learn more about tapable library checkout tapable repository.

Stay tuned for more stories like this

Follow me on Twitter

8KC5OLsx_400x400.jpg

Bohdan Liashenko

@bliashenko
Code geek. I tweet about interesting code tips and findings I am discovering under the hood of popular open source projects.

Join the Newsletter

Join the newsletter to receive interesting code tips and findings I am discovering under the hood of popular open source projects.

Your email address

About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK