I have a Gatsby site that consumes a number of packages. One of those packages is published from our monorepo: @example/forms
. That package contains a number of named exports, one for each form component that we use on our site. There are quite a large number of forms and some are relatively complex multistep forms of a non-trivial size.
Currently, Gatsby/Webpack does a good job of treeshaking, and does produce a large number of bundles, some common bundles and one for each page of the site, containing any local assets or components that are only used on that page. However, all the components from @example/forms
are being added to the commons
bundle, despite the fact that most are used on only a single page. This is causing unnecessary bloat of the commons
bundle which is loaded on every page.
If feels like it should be possible for the individual components within @example/forms
to be split out into the page-specific bundles, but I'm not sure if I'm hoping for too much. For example, if Form A is only used on page 4, I'd like Form A to only be added to the bundle for page 4.
Is this something that is supported, and if so, what could be preventing this from happening.
@example/forms
is transpiled with ES6 exports left intact, and sideEffects
is set to false
in its package.json
.
Its main
file is index.js
which (re)exports all the form components from their own files as separate named exports:
export {default as FormA} from './forms/formA'
export {default as FormB} from './forms/formB'
...
Another thing that might be relevant is that all the exports from @example/forms
are used within the Gatsby site, just on separate pages. It appears that perhaps tree-shaking cannot be used across bundles, i.e. tree shaking is performed first, then what is left is split into bundles. Using that logic, @example/forms
would have been used on multiple pages and would be moved to commons. However this is definitely not optimal, and hopefully isn't what is happening.
Reduce your bundle size by code-splitting Instead of keeping all your code in the one bundle, you can split it up into multiple bundles to be loaded separately. This is known as code-splitting. If your app has multiple pages, an easy candidate for code-splitting is to split up the code by each page.
Code splitting allows you to break up the monolithic bundle into various smaller bundles. You can then load the bundles in parallel or implement lazy loading, delaying download of certain code until a user needs it.
Code splitting is one of the most compelling features of webpack. This feature allows you to split your code into various bundles which can then be loaded on demand or in parallel.
Code-Splitting is a feature supported by bundlers like Webpack, Rollup and Browserify (via factor-bundle) which can create multiple bundles that can be dynamically loaded at runtime.
Tree-shaking process is part of the minification step which is really at late stage, since that it is not possible to reverse the order, first tree-shake then chunk split.
But you can split your forms lib before hand using some heuristics
splitChunks
in order to split this @example/forms
module into a separate chunk as whole, this will decrease the commons bloat & on the first form usage all forms will be loaded.// webpack.config.js
module.exports = {
...
optimization: {
splitChunks: {
cacheGroups: {
formComponents: {
chunks: 'all',
enforce: true,
minChunks: 1,
name: 'form-components',
priority: 10,
test: /[\\\/]node_modules[\\\/]@example[\\\/]forms[\\\/]/,
},
},
},
},
...
};
@example/forms
module based on some heuristics, in my example I'm "grouping" all items based on the form path.// webpack.config.js
module.exports = {
...
optimization: {
splitChunks: {
cacheGroups: {
formComponents: {
chunks: 'all',
minChunks: 1,
name(module) {
const libPath = path.resolve(path.join('node_modules/@example/forms'))
const folderBasedChunk = path.relative(libPath, module.context);
const hash = crypto.createHash('sha1');
hash.update(folderBasedChunk)
return hash.digest('hex').substring(0, 8)
},
priority: 10,
reuseExistingChunk: true,
enforce: true,
test: /[\\\/]node_modules[\\\/]@example[\\\/]forms[\\\/]src[\\\/]forms[\\\/]/,
},
},
},
...
};
You can checkout a small example that I've created that mimics the scenario. https://github.com/felixmosh/webpack-module-split-example
Hope this helps
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With