I recently wrote about code splitting with Webpack and using the SplitChunksPlugin. In that blog post, I touched upon how Webpack might create chunks based on common files that are shared between other chunks.
Let's say your app has two features.
- Feature A
- Feature B
And you've used code splitting to reduce your bundle size, and now expect to have three bundles.
app.js
feature-a.bundle.js
feature-b.bundle.js
But if a few of your components are shared between these bundles, this code will be duplicated. To keep your bundles small, you can use the SplitChunksPlugin
to split out these common bundles rather than duplicating your code.
These shared bundles might get random names or numbers given to them, like 981.bundle.js
.
And that's all ok, but when you make changes, those names might change. The code could disappear. And your users could end up with a ChunkLoadError.
If you just want to know how to name these common chunk bundles, scroll down to "Naming common chunks".
Naming your entry file
Naming your entry file, like app.js
in Webpack is fairly simple, using a webpack.config.js
file.
const path = require('path');
module.exports = {
entry: './src/app.js',
output: {
filename: 'app.bundle.js',
path: path.resolve(__dirname, 'dist'),
},
};
The above configuration would give you an output bundle called app.bundle.js
. And that works great. Until your Webpack bundle gets too big. Then you need to split up your code into smaller chunks (known as code splitting).
You can add multiple entry points if you need to, give the entry points names, and provide a [name]
output pattern to generate the name. Here's the set-up to do this in webpack.config.js
straight from the Webpack docs.
const path = require('path');
module.exports = {
entry: {
app: './src/app.js',
print: './src/print.js',
},
output: {
filename: '[name].bundle.js',
path: path.resolve(__dirname, 'dist'),
},
};
Now you get two output bundles called app.bundle.js
and print.bundle.js
.
But this is all very manual. And only applies to entry points. What if app.bundle.js
was getting big, and you want to split that up into multiple bundles? With dynamic code splitting, you can stick with one entry point and split the rest of the code inside your app. But how do you name those bundles?
Naming your dynamic split chunks
You can use dynamic imports in your code to tell Webpack to split the imported code into a separate chunk.
I'm not going to go into great detail about code splitting, because this post is about naming your chunks rather than creating them. If you want to learn more about code splitting and how to do it, check out How to reduce your ReactJS bundle size with Webpack code splitting.
As a basic guide, your imports would go from this:
import { featureA } from './featureA';
To this:
import("./featureA").then(result => {
// do something
});
Or you might use a feature of the framework you are using. For example, if you are using ReactJS, you might use React.lazy()
.
const FeatureA = React.lazy(() => import('./FeatureA'));
But these chunks have no name!
To name these dynamic chunks, you can add a pattern to output.chunkFilename
in webpack.config.js
.
output: {
filename: '[name].bundle.js',
chunkFilename: '[name].chunk.bundle.js',
path: path.resolve(__dirname, 'dist'),
},
And pass a webpackChunkName
comment to Webpack with your import.
import(/* webpackChunkName: "featureA" */ './featureA')
Now Webpack knows what to name your chunks, and you can better understand what each bundle of code represents.
You might also want to use a [contenthash]
in the file name for cache-busting reasons!
Naming your common chunks
Remember at the start of this blog post how we discussed that Webpack might split your code even further?
Before Webpack 4, there was a CommonsChunkPlugin
that would automatically name these "common" chunks with the names of the chunks they are used by.
Let's say your code was used in both feature A a
and feature B b
chunks. Your common chunk would be called a-b
. If you also had a feature C c
and some code across all three features, you would get another chunk named a-b-c
.
I found this naming pattern pretty useful because I could know where the chunks were being used in my application.
But in Webpack 4 and up, this naming pattern stopped happening with the new SplitChunksPlugin
. But you can get it back!
You just need to add a name function to the settings, which you can include in your webpack.config.js
under optimization
.
// ...
optimization: {
splitChunks: {
name: (module, chunks, cacheGroupKey) => {
const allChunksNames = chunks.map((chunk) => chunk.name).join('-');
return allChunksNames;
},
},
},
};
Naming your vendor chunks
You're probably not just bundling up your code in Webpack, but other people's codes too. Maybe a framework like ReactJS, or a utility library like Lodash.
If your separate out these modules to improve browser caching and minimise the impact of future updates, it's helpful to name these code chunks too.
I covered more about why you would separate frameworks like ReactJS in my last blog post how to reduce your ReactJS bundle size with Webpack code splitting.
To name these chunks, you can use optimization.splitChunks
again, but this time, cacheGroups
.
optimization: {
splitChunks: {
// ...
cacheGroups: {
reactVendor: {
test: /[\\/]node_modules[\\/](react|react-dom|react-router-dom)[\\/]/,
name: 'vendor-react',
chunks: 'all',
},
corejsVendor: {
test: /[\\/]node_modules[\\/](core-js)[\\/]/,
name: 'vendor-corejs',
chunks: 'all',
},
},
},
},
You create a group, tell it what to include with some Regex in test
, and give it a name
e.g. vendor-react
.
And that's how you can name your Webpack chunks, whether they are dynamically imported, or created by Webpack on-demand as common bundles.
You can learn more about code splitting and how to minimise your ReactJS bundles in How to reduce your ReactJS bundle size with Webpack code splitting.