Description
When reading https://webpack.js.org/guides/code-splitting-libraries/ I was trying to figure out how to create a new file based on the common parts of two entry points.
I guess this is technically what the "vendor" example is doing, but as a first-time reader I didn't realise this, because this is in a page specific to "libraries", whereas I was wanting to avoid duplication in code I'd written.
I don't know if this would help people other than me, but I would have understood the guide better if it was structured something like this:
(the following is just showing the order I think things should be introduced, not final copy)
Splitting out common code
If two of your entry points import/require the same modules, you'll end up with that code duplicated in each output file. CommonsChunkPlugin
is a plugin that can extract common modules:
var webpack = require('webpack');
var path = require('path');
module.exports = function(env) {
return {
entry: {
page1: './page1/index.js',
page2: './page2/index.js'
},
output: {
filename: '[name].[chunkhash].js',
path: path.resolve(__dirname, 'dist')
},
plugins: [
new webpack.optimize.CommonsChunkPlugin({
name: 'common' // Specify the common bundle's name.
})
]
}
};
This will generate:
page1.ef0942b9ff95bd25c024.js
- the stuff unique to page1.page2.9de799314948aa4d7686.js
- the stuff unique to page2.common.a7b0b08b0c83bdccebbd.js
- the stuff used in both page1 and page2.
Note: The filename of the chunk will inherit the filename of the output. [name].[chunkhash].js
in this case.
page1
won't automatically load common
, you must include both on your page, and common
must execute first:
<script src="common.a7b0b08b0c83bdccebbd.js" defer></script>
<script src="page1.ef0942b9ff95bd25c024.js" defer></script>
Splitting out webpack's bootstrap
Webpack's bootstap code for dealing with importing modules is usually included with every output. However, CommonsChunkPlugin
will see this duplication and move it into one file, common
in this case.
However, since the bootstrap contains details about the other modules, such as their names & hashes, updating the code unique to page1
will also update common
, since page1
's hash will change. Adding a page3
will also update common
since it will be adding details of this new module. This means the user is having to download a fresh common
even though only a small part of the bootstrap has changed.
To solve this, we can extract the bootstrap into its own file:
var webpack = require('webpack');
var path = require('path');
module.exports = function(env) {
return {
entry: {
page1: './page1/index.js',
page2: './page2/index.js'
},
output: {
filename: '[name].[chunkhash].js',
path: path.resolve(__dirname, 'dist')
},
plugins: [
new webpack.optimize.CommonsChunkPlugin({
name: 'common' // Specify the common bundle's name.
}),
new webpack.optimize.CommonsChunkPlugin({
name: 'manifest'
})
]
}
};
This will generate:
page1.ef0942b9ff95bd25c024.js
- the stuff unique to page1.page2.9de799314948aa4d7686.js
- the stuff unique to page2.common.a7b0b08b0c83bdccebbd.js
- the stuff used in both page1 and page2.manifest.1302ca8eccf18a208f11.js
- webpack's bootstrap and module manifest.
Instances of CommonsChunkPlugin
are handled in order. Our first instance extracts all the common code to common
. The second instance doesn't find any common code (we've already extracted it), but it will generate a file containing the bootstrap, and remove it from the others.
Now manifest
must execute first:
<script src="manifest.1302ca8eccf18a208f11.js" defer></script>
<script src="common.a7b0b08b0c83bdccebbd.js" defer></script>
<script src="page1.ef0942b9ff95bd25c024.js" defer></script>
Splitting out rarely-updated modules
We've removed all of the duplication, but you may find that a small part of one of your bundles is updating frequently. It's best to extract that, so the rest of the bundle, which perhaps updates infrequently, can cache for longer.
To do this, create an additional entry point for the parts that frequently update (or the parts that infrequently update – the important thing is they're separate):
var webpack = require('webpack');
var path = require('path');
module.exports = function(env) {
return {
entry: {
page1: './page1/index.js',
page2: './page2/index.js',
fequentlyupdates: './page1/frequentlyupdates.js'
},
output: {
filename: '[name].[chunkhash].js',
path: path.resolve(__dirname, 'dist')
},
plugins: [
new webpack.optimize.CommonsChunkPlugin({
names: ['common', 'fequentlyupdates']
}),
new webpack.optimize.CommonsChunkPlugin({
name: 'manifest'
})
]
}
};
CommonsChunkPlugin
sees that fequentlyupdates
is the name of an entry point, so it removes its code from the other modules.
Note: I expected two instances of CommonsChunkPlugin
to work. One for common
, then one for fequentlyupdates
, but it throws an error. I don't know if this is a bug with webpack or a bug in my mental model.
Splitting out a library
Library modules are typically rarely updated as part of your build. So let's say instead of fequentlyupdates
, you wanted to extract moment
, the date formatting library:
var webpack = require('webpack');
var path = require('path');
module.exports = function(env) {
return {
entry: {
page1: './page1/index.js',
page2: './page2/index.js',
moment: 'moment'
},
output: {
filename: '[name].[chunkhash].js',
path: path.resolve(__dirname, 'dist')
},
plugins: [
new webpack.optimize.CommonsChunkPlugin({
names: ['common', 'moment']
}),
new webpack.optimize.CommonsChunkPlugin({
name: 'manifest'
})
]
}
};
Here we've replaced the path to the fequentlyupdates
script with the module name.
Splitting out everything in node_modules
Assuming you update dependencies less-frequently than your application code, you may want to split everything within node_modules
:
var webpack = require('webpack');
var path = require('path');
module.exports = function(env) {
return {
entry: {
page1: './page1/index.js',
page2: './page2/index.js'
},
output: {
filename: '[name].[chunkhash].js',
path: path.resolve(__dirname, 'dist')
},
plugins: [
new webpack.optimize.CommonsChunkPlugin({
name: 'common'
}),
new webpack.optimize.CommonsChunkPlugin({
name: 'vendor',
minChunks: function (module) {
return module.context && module.context.indexOf('node_modules') !== -1;
}
}),
new webpack.optimize.CommonsChunkPlugin({
name: 'manifest'
})
]
}
};