Optimizing the "Commons" Chunk
Keep on Learning!
If you liked what you've learned so far, dive in! Subscribe to get access to this tutorial plus video, code and script downloads.
With a Subscription, click any sentence in the script to jump to that part of the video!
Login SubscribeHere's how I like to configure the CommonsChunkPlugin. Set minChunks
to Infinity
. Then change the name
option to layout
:
// ... lines 1 - 30 | |
module.exports = { | |
// ... lines 32 - 93 | |
plugins: [ | |
// ... lines 95 - 105 | |
new webpack.optimize.CommonsChunkPlugin({ | |
// "layout" is an entry file | |
// anything included in layout, is not included in other output files | |
name: 'layout', | |
minChunks: Infinity | |
}) | |
], | |
// ... line 113 | |
}; |
That name is important. Scroll up: layout
is the name of one of our entries!
// ... lines 1 - 30 | |
module.exports = { | |
entry: { | |
// ... lines 33 - 34 | |
layout: './assets/js/layout.js', | |
}, | |
// ... lines 37 - 93 | |
plugins: [ | |
// ... lines 95 - 105 | |
new webpack.optimize.CommonsChunkPlugin({ | |
// "layout" is an entry file | |
// anything included in layout, is not included in other output files | |
name: 'layout', | |
minChunks: Infinity | |
}) | |
], | |
// ... line 113 | |
}; |
Because of this, anything that is required in layout.js
will not be included in other files. Yep, since layout.js
requires jquery
, it will live in the compiled layout.js
, but not in login.js
or rep_log.js
:
// ... lines 1 - 2 | |
import $ from 'jquery'; | |
import 'bootstrap-sass'; | |
// ... lines 5 - 12 |
And since bootstrap-sass
is used here, it also won't be output in any other built file.
Our layout.js
file now serves two purposes. First, to collect all the common modules that we don't want duplicated across multiple entries. And second, as a way for us to execute any global JavaScript we might have.
Ok, find your Webpack terminal and stop it. Then, clear out the build directory:
rm -rf web/build/*
to delete that vendor.js
file. Now, Restart webpack:
./node_modules/.bin/webpack --watch
Yep, no vendor.js
. In base.html.twig
, we can remove the extra vendor.js
script tag:
// ... lines 1 - 94 | |
{% block javascripts %} | |
// ... lines 96 - 97 | |
<script src="{{ asset('build/vendor.js') }}"></script> | |
// ... line 99 | |
{% endblock %} | |
// ... lines 101 - 104 |
Try it out: refresh! Yes! Everything works!
The Webpack Manifest
Look inside the built layout.js
file. Right on top, you can see the Webpack bootstrap: a collection of functions that the rest of the built code use to help organize all the Webpack module-loading magic. Before we used CommonsChunkPlugin, this appeared at the top of every output file. But now - as you can see in the built login.js
- it's not there anymore. Webpack is already smart enough to know that since layout.js
is our common chunk and will be included on every page, the Webpack bootstrap code doesn't need to be repeated. Clever Bootstrap!
But... there's one small catch. Isn't there always one small catch? The Webpack bootstrap code includes something called the manifest. Internally, Webpack gives each module a number id, and the manifest contains those ids. Sometimes, those ids change.
Normally, we don't care! These are all internal Webpack details! But... if the module ids change... then the manifest changes... and that means that the contents of layout.js
change.
Let me say it a different way: because of the module ids in the manifest, if I make a change to, say, login.js
, it may cause the built layout.js
file to change. Why is that a problem? Caching.
We're going to talk more about caching and versioning later. But for now, let me just say this: you don't want the contents of a built file to change unless it actually needs to change. Why make your user re-download a fresh layout.js
file... if nothing important changed inside it?
Extracting the Manifest
Anyways, the fix is to move that Webpack bootstrap code out of layout.js
... so it can happily remain unchanged even when the internal module ids change. To do that, open webpack.config.js
and go to the CommonsChunkPlugin
. Change the name
option to an array, with layout
and a new entry called manifest
:
// ... lines 1 - 30 | |
module.exports = { | |
// ... lines 32 - 93 | |
plugins: [ | |
// ... lines 95 - 105 | |
new webpack.optimize.CommonsChunkPlugin({ | |
name: [ | |
// ... lines 108 - 109 | |
'layout', | |
// ... line 111 | |
'manifest' | |
], | |
// ... line 114 | |
}) | |
], | |
// ... line 117 | |
}; |
I'll move my first comment down a bit, and then add a new comment above manifest
:
Dumps the manifest in a separate file.
// ... lines 1 - 30 | |
module.exports = { | |
// ... lines 32 - 93 | |
plugins: [ | |
// ... lines 95 - 105 | |
new webpack.optimize.CommonsChunkPlugin({ | |
name: [ | |
// "layout" is an entry file | |
// anything included in layout, is not included in other output files | |
'layout', | |
// dumps the manifest into a separate file | |
'manifest' | |
], | |
// ... line 114 | |
}) | |
], | |
// ... line 117 | |
}; |
Let's see what this does! Restart Webpack:
./node_modules/.bin/webpack --watch
Ok, we still have a layout.js
file... but now we also have a tiny manifest.js
! Thanks to this change, layout.js
is just our code. And the tiny manifest.js
contains the Webpack bootstrap. If the ids change now, the user will only need to re-download that small file.
Of course, to get this to work... we need to add another script tag! Open base.html.twig
. Add a second script
tag pointing to manifest.js
:
// ... lines 1 - 94 | |
{% block javascripts %} | |
// ... lines 96 - 97 | |
<script src="{{ asset('build/manifest.js') }}"></script> | |
<script src="{{ asset('build/layout.js') }}"></script> | |
{% endblock %} | |
// ... lines 101 - 104 |
Phew! I know, that was confusing. But our setup rocks! Thanks to CommonsChunkPlugin
, anything in layout.js
, will not be duplicated in the other files.
Next! Let's learn about the webpack-dev-server.
Here is my config for Webpack 4.
This reduces the file sizes but the css can't be found when I go to the login page. And it does not find the css after logging in. Remove the optimization and everything returns to normal. What am I missing?