We have been using Webpack forever (2015 to be exact).
We tried to move to vite, snowpack, esbuild, bun, turbopack, but none of them can bundle everything that we need to bundle yet, like our backends, frontends, widgets, plugins, and scripts.
As we can't leave Webpack, we found many ways to optimize it.
Making it faster and to consume fewer memory.
Making it fast and consuming less memory
The first thing you can do is to have different configurations for development and production.
We use webpack-merge
to merge our common
configurations that are the same for development and production.
We have the following structure
webpackCommonConfig.ts
webpackDevConfig.ts
webpackProdConfig.ts
Source Map
We need to compile modern JavaScript code to be compatible with older browsers.
The source map does the mapping from the generated code to the source code. This is useful for debugging, development and error monitoring in production.
We can use a faster source map in development to speed up bundling.
devtool: 'cheap-module-source-map',
cheap-module-source-map
provides the best cost benefit for source map in development.
Pathinfo
output: {
pathinfo: false,
},
pathinfo: false
will output fewer comments in bundles making bundling faster.
Babel-loader vs esbuild-loader vs SWC-Loader
Deciding what loader will compile your TypeScript code can speed up bundling a lot.
Babel is the default one, but it is slow.
Esbuild and SWC are faster, but only SWC has a Relay plugin that we need.
So, we use swc-loader to compile our TypeScript code.
writeToDisk: true
devServer: {
devMiddleware: {
writeToDisk: true,
},
}
Webpack Dev Server will default to bundling everything in memory.
This is faster, but if you have a very big frontend as we have, it will consume a lot of memory.
One of our frontends was consuming more than 5GB of RAM, when we switched to writeToDisk: true
, it started consuming fewer than 1GB of RAM.
We didn't notice any performance loss with this change.
The computer became even more responsive with less memory pressure.
Persistent cache
cache: {
type: 'filesystem',
},
We opt-in Webpack persistent cache using the filesystem, to avoid consuming a lot of memory, and to also speed up rebuilds.
If we stop our Webpack bundling process and start it over, it will catch up from the last cache point.
Lazy Compilation
experiments: {
lazyCompilation: {
imports: true,
entries: true,
},
cacheUnaffected: true,
},
Lazy Compilation will lazy bundle/compile entry points and dynamic imports.
This will make your bundle compile faster, and it will also use less memory as we need to bundle less.
Every page/route is a dynamic import, so it is lazily compiled at Woovi.
This makes sure we only compile the routes we are working on.
Key Takeaways
Every optimization was done over time.
We dig into documentation, issues, articles, and code to find the right configuration that fit better our use cases.
We also used memory and CPU profiles to find bottlenecks to focus our optimization efforts.
I do believe there is even more optimization to be done.
If you have even more Webpack or bundling tricks comment on the article.
Woovi
Woovi is a Startup that enables shoppers to pay as they like. To make this possible, Woovi provides instant payment solutions for merchants to accept orders.
If you want to work with us, we are hiring!
Photo by Zoltan Tasi on Unsplash
Top comments (2)
Amazing post! Thank for shared!
I also like to use this pattern for webpack files:
for optimization, using splitChunks for dynamic routes in React/Vue.js mainly on my my micro-frontends :)
and for a better babel loader I use options cacheDirectory true.
As usual, dev.to posts providing concrete and insightful tips instead of generic nonsense. I love this kind of content.