The idea behind bundle splitting is pretty simple. If you have one giant file, and change one line of code, the user must download that entire file again. But if you’d split it into two files, then the user would only need to download the one that changed, and the browser would serve the other file from cache. It’s worth noting that since bundle splitting is all about caching, it makes no difference to first time visitors.
- Webpack has some clever defaults which we override here, like a maximum of 3 files when splitting the output files, and a minimum file size of 30 KB (all smaller files would be joined together).
- cacheGroups is where we define rules for how Webpack should group chunks into output files. We have one here called ‘vendor’ that will be used for any module being loaded from node_modules. Normally, you would just define a name for the output file as a string. But we're defining name as a function (which will be called for every parsed file) then returning the name of the package from the path of the module. As a result, we’ll get one file for each package, e.g. npm.react-dom.899sadfhj4.js.
- NPM package names must be URL-safe in order to be published, so we don’t need to encodeURI that packageName. But some servers don't like
@symbol. - This whole setup is great because it’s set-and-forget. No need to refer to any package by name.
- To extract any particular features/pages we manually add some entry points, telling Webpack to create a file for each of those items. Webpack will also create files for things that are shared between LeFeature and LePage, so that we don’t get duplicated code.
module.exports = {
entry: {
main: path.resolve(__dirname, 'src/index.js'),
LeFeature: path.resolve(__dirname, 'src/LeFeature/LeFeature.js'),
LePage: path.resolve(__dirname, 'src/LePage/LePage.js'),
},
output: {
path: path.resolve(__dirname, 'dist'),
filename: '[name].[contenthash].js',
chunkFilename: '[name].chunk.js',
hashDigestLength: 8,
},
optimization: {
runtimeChunk: 'single',
// `deterministic` ensures name in output.chunkFilename
// doesn't change between compilations if content hasn't changed;
// name will be assigned a collision-safe contenthash with shortest possible length (good for caching)
chunkIds: 'deterministic',
moduleIds: 'deterministic',
splitChunks: {
chunks: 'all',
maxInitialRequests: Infinity,
minSize: 0,
cacheGroups: {
vendor: {
test: /[\\/]node_modules[\\/]/,
name(module) {
// get the name (e.g. node_modules/packageName/skip/this/part.js)
// or node_modules/packageName
const packageName = module.context.match(/[\\/]node_modules[\\/](.*?)([\\/]|$)/)[1];
// npm package names are URL-safe, but some servers don't like @ symbols
return `npm.${packageName.replace('@', '')}`;
},
},
},
},
},
};