Unlock Webpack splitChunks & Manifest: A Hands‑On Guide to Smarter Bundling
This tutorial walks through the fundamentals of Webpack’s splitChunks and manifest features, explaining modules, chunks, and bundles, detailing configuration options like chunks, cacheGroups, minChunks, priority, maxInitialRequests, and maxAsyncRequests, and shows practical code examples and visual results to help developers master efficient asset bundling.
1 Background
For anyone new to building projects, Webpack often feels like an insurmountable hurdle because of its abstract concepts and concise documentation.
The author rarely writes Webpack configurations from scratch, so some settings are not completely clear.
A recent requirement to add MD5 version numbers to page resources gave an opportunity to revisit and reorganize the project's Webpack configuration.
This article skips basic concepts such as entry and output and focuses on the splitChunks and manifest sections.
2 Basic Concepts
To understand
splitChunks, you need to grasp three core ideas: module, chunk, and bundle.
module : each imported file is a module (the code you write yourself).
chunk : when source files are fed to Webpack, it generates chunks based on import relationships.
bundle : the final compressed output that can be executed directly.
An illustration of these concepts:
Chunks come in three types:
Project entry (entry).
Dynamically imported code.
Code split out by
splitChunks.
3 splitChunks Overview
splitChunksis used to split bundles, extracting code that meets certain rules into separate packages to isolate common modules and reduce duplication.
The configuration items inside
splitChunksdefine the splitting rules; the
cacheGroupsoption must satisfy all its conditions to take effect.
In Webpack 5, the default configuration for
splitChunksis:
<code>module.exports = {
//...
optimization: {
splitChunks: {
chunks: 'async',
// Only split when the content exceeds minSize
minSize: 20000,
// Ensure the remaining chunk after splitting is larger than this value to avoid zero‑size modules
minRemainingSize: 0,
minChunks: 1,
maxAsyncRequests: 30,
maxInitialRequests: 30,
// Force split when size exceeds this threshold, ignoring minRemainingSize, maxAsyncRequests, maxInitialRequests
enforceSizeThreshold: 50000,
cacheGroups: {
vendors: {
test: /[\\/]node_modules[\\/]/,
priority: -10,
// Reuse an existing chunk if it has already been split from the main bundle
reuseExistingChunk: true,
},
default: {
minChunks: 2,
priority: -20,
reuseExistingChunk: true,
},
},
},
},
};
</code>The following sections explain the configuration items that were not commented.
3.1 chunks
The
chunksoption can be
all,
async, or
initial; the default is
async.
Consider this example with
entry1.jsthat dynamically imports
page1.js(React syntax requires Babel).
entry1.js
<code>import React from 'react';
import ReactDom from 'react-dom';
const App = () => {
let Page1 = null;
import('./page1.js').then(comp => {
Page1 = comp;
});
return (
<div>
<div>App</div>
<Page1 />
</div>
)
}
ReactDOM.render(<App />, document.getElementById('root'))
</code>page1.js
<code>import _ from 'lodash';
import React from 'react';
const Page1 = () => {
return (
<div>
<div>Page1</div>
</div>
)
}
export default Page1;
</code>Bundling result (illustrated below):
Analysis:
main.jsis the entry file and is split into its own chunk.
page1_js.jsis the dynamically loaded file, also a separate chunk.
The third chunk contains the third‑party library
lodash, extracted according to the
cacheGroupsconfiguration.
Why is
lodashextracted while
react, which is imported by both
entry1.jsand
page1.js, is not? The answer lies in the
chunks: "async"setting, which only applies to asynchronously loaded packages.
Because
lodashis imported in
page1.js, which is loaded asynchronously, it meets the split rule.
reactis not loaded asynchronously, so it does not trigger the split.
Understanding
chunks: "async"makes it easy to grasp the behavior of
chunks: "all"and
chunks: "initial":
initialsplits only from entry modules.
allsplits from both entry and async modules.
3.2 cacheGroups
cacheGroupsis the core of
splitChunks. Each group defines a set of rules for extracting modules. The default
vendorsgroup matches
node_modules, so third‑party libraries are placed into a vendor chunk. The
defaultgroup extracts shared user‑defined modules.
Key options in
cacheGroups:
3.2.1 minChunks
A module is extracted when it is imported at least
minChunkstimes, but only counting imports from entry modules (dynamic imports are ignored unless
chunksis set to
all).
Example: both
entry1.jsand
entry2.jsimport a local
jquery.js. Since the import count is 2,
jquerysatisfies
minChunksand is extracted.
When only
entry1.jsremains, the dynamic import of
jqueryin
page1.jsdoes not count toward
minChunks, so
jquerystays in the entry chunk.
3.2.2 priority
The
priorityfield determines which rule wins when a chunk satisfies multiple groups. Higher priority wins; if equal, the first defined group wins. This explains why
react-domends up in the
vendorschunk instead of the
defaultchunk.
3.3 maxInitialRequests
This option limits the maximum number of parallel requests for entry files. It prevents excessive chunk splitting that would cause too many HTTP requests.
When multiple chunks qualify for extraction but the limit is reached, Webpack selects the larger chunk to split.
3.4 maxAsyncRequests
Similar to
maxInitialRequests, but applies to asynchronous modules. It limits the number of parallel async requests.
4 manifest
When a new version of a page is deployed, browsers may still serve cached resources unless the filenames contain a hash. Adding a
contenthashto the filename solves this, but when one project imports assets built by another project, the hash changes each build, making it hard to know which file to request.
The
manifestplugin records the mapping between original filenames and their hashed counterparts, enabling accurate requests for versioned assets.
Example manifest generated by
webpack-manifest-plugin:
5 Summary
By experimenting with concrete demos, even a daunting tool like Webpack becomes much more approachable. When you encounter a technology that feels intimidating, it signals a knowledge gap—spend time mastering it, and the next encounter will feel like an opportunity to showcase your expertise.
Tencent IMWeb Frontend Team
IMWeb Frontend Community gathering frontend development enthusiasts. Follow us for refined live courses by top experts, cutting‑edge technical posts, and to sharpen your frontend skills.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.