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 SubscribeYou've made it to the end! So let's do something fun!
The cool thing about Webpack is that it allows you to just code: import modules when you need them, import CSS and let Webpack figure out how to package it all together.
The downside is that you might not realize if your built files are becoming too big! Like, maybe you import a huge library... but only use a small part of it. Or, you have some big code that can be code split. There's a fun solution to seeing these optimizations!
Google for Webpack visualizer. The one I like is by someone named Chris Bateman. Hi Chris!
When you execute Webpack, you can tell it to dump a JSON file with very detailed information about the final assets. Find your terminal and stop webpack. Use the full, long version of our production command: NODE_ENV=production ./node_modules/.bin/webpack
, and then --json
. Save the output to a stats.json
file:
NODE_ENV=production ./node_modules/.bin/webpack --json > stats.json
Run it! Webpack compiles like normal... but it outputs JSON, which we save to stats.json
. Check out that file: it has details about errors, warnings, and endless information about the assets.
Sweet! Now: back to the browser! Click the box to load our stats.json
.
Wow! A donut! It looks delicious! Apparently, 97.6% of our delicious donut... I mean 97.6% of our code is coming from stuff in node_modules/
. Only this tiny portion comes from our assets/
directory! As you move out, it describes each part in more and more detail.
Oh, and notice: we're looking at all chunks. But you can also view this graph for only specific JavaScript files. This 02d
file is actually our code split chunk!
But let's look at everything. Inside node_modules/
, jquery
takes up 39%. The second biggest portion... is something called core-js
, at 29.5%! That's interesting... more on that in a moment.
Next is bootstrap-sass
, sweetalert
and then css-loader
... which if you look closer is Font Awesome. A few smaller libraries finish off the donut.
So... this contained a surprise! The second biggest library is core-js
. What the heck is that? And where did it come from?
If you do some digging, you'll find out that this comes from layout.js
: specifically from babel-polyfill
:
... lines 1 - 5 | |
import 'babel-polyfill'; | |
... lines 7 - 12 |
Remember, we included this so that it would polyfill the Promise
object for older browsers.
I'll hold Command
to click into the babel-polyfill
module. Huh, this basically just uses another library: core-js
. And if you dig further, you'd find out that by requiring the shim
module, it imports many polyfills. Yep, we're importing a huge library just to polyfill Promise
.
Let's be smarter! First, remove babel-polyfill
:
yarn remove babel-polyfill
And now add core-js
:
yarn add core-js --dev
While we're waiting, Google for core-js
. Ok, this is a library full of polyfills, for ES6, ES7 and other things. Ah, and here are the docs about Promise
.
See the "CommonJS entry points" part? This is telling us that if we need the Promise
polyfill, we can just require core-js/library/es6/promise
or core-js/library/fn/promise
.
Let's do that! In layout.js
, remove the babel-polyfill
line. Instead, import core-js/library/es6/promise
:
... lines 1 - 5 | |
import 'core-js/library/es6/promise'; | |
... lines 7 - 12 |
And now... re-dump our stats:
NODE_ENV=production ./node_modules/.bin/webpack --json > stats.json
When it finishes, find the visualizer and refresh to reset it. Load the new stats.json
. Oh, way better! jQuery now takes up 53%... not because it got bigger, but because our JavaScript donut is so much smaller. Then bootstrap-sass
& sweetalert2
. core-js
went from 29.5% to only 8.1%.
Thank you visualizer! So, focus on your code! And then... come to the visualizer to make sure you're not doing anything crazy.
Ok guys! We've done it! We're done! Thank you for staying with me! Webpack is an amazing library... but it's hard. It gives you so much flexibility... which means I have endless ways to confuse myself and mess things up! But now, you are a Webpack power user!
Oh, and also check out Webpack Encore: a library created by Symfony to make configuring Webpack easier, faster and more fool-proof. We'll create a tutorial about it on KnpU soon!
All right, guys. See you next time.
Hey Matt,
Thanks for the kind words! We're happy you like it and our service was useful for you! Please, come back anytime you want ;)
Cheers!
This chapter made me wonder - are there any ways to... em... tree-shake the polyfill module automatically? Because that looks just like what WebPack do when it tree-shake the javascript.
Hey Ivan!
Yep! And, as far as I can tell (which is a pretty good amount of research), this should happen automatically (as long as you are doing the "import" syntax to get only what you need - e.g. import { Promise } from 'babel-polyfill'
. But, this doesn't seem to work reliably. However, in Webpack 4, a lot of improvements have been made - so it may be more reliable there.
But, short answer, yes! But only if you import things correctly, and then... it may not work - so just make sure you watch it closely ;).
Cheers!
// composer.json
{
"require": {
"php": "^7.2.0",
"symfony/symfony": "3.3.*", // v3.3.16
"twig/twig": "2.10.*", // v2.10.0
"doctrine/orm": "^2.5", // v2.7.0
"doctrine/doctrine-bundle": "^1.6", // 1.10.3
"doctrine/doctrine-cache-bundle": "^1.2", // 1.3.5
"symfony/swiftmailer-bundle": "^2.3", // v2.6.3
"symfony/monolog-bundle": "^2.8", // v2.12.1
"symfony/polyfill-apcu": "^1.0", // v1.4.0
"sensio/distribution-bundle": "^5.0", // v5.0.22
"sensio/framework-extra-bundle": "^3.0.2", // v3.0.26
"incenteev/composer-parameter-handler": "^2.0", // v2.1.2
"friendsofsymfony/user-bundle": "^2.0", // v2.1.2
"doctrine/doctrine-fixtures-bundle": "~2.3", // v2.4.1
"doctrine/doctrine-migrations-bundle": "^1.2", // v1.3.2
"composer/package-versions-deprecated": "^1.11", // 1.11.99
"friendsofsymfony/jsrouting-bundle": "^1.6" // 1.6.0
},
"require-dev": {
"sensio/generator-bundle": "^3.0", // v3.1.6
"symfony/phpunit-bridge": "^3.0" // v3.3.5
}
}
// package.json
{
"dependencies": [],
"devDependencies": {
"babel-core": "^6.25.0", // 6.25.0
"babel-loader": "^7.1.1", // 7.1.1
"babel-plugin-syntax-dynamic-import": "^6.18.0", // 6.18.0
"babel-preset-env": "^1.6.0", // 1.6.0
"bootstrap-sass": "^3.3.7", // 3.3.7
"clean-webpack-plugin": "^0.1.16", // 0.1.16
"copy-webpack-plugin": "^4.0.1", // 4.0.1
"core-js": "^2.4.1", // 2.4.1
"css-loader": "^0.28.4", // 0.28.4
"extract-text-webpack-plugin": "^3.0.0", // 3.0.0
"file-loader": "^0.11.2", // 0.11.2
"font-awesome": "^4.7.0", // 4.7.0
"jquery": "^3.2.1", // 3.2.1
"lodash": "^4.17.4", // 4.17.4
"node-sass": "^4.5.3", // 4.5.3
"resolve-url-loader": "^2.1.0", // 2.1.0
"sass-loader": "^6.0.6", // 6.0.6
"style-loader": "^0.18.2", // 0.18.2
"sweetalert2": "^6.6.6", // 6.6.6
"webpack": "^3.4.1", // 3.4.1
"webpack-chunk-hash": "^0.4.0", // 0.4.0
"webpack-dev-server": "^2.6.1", // 2.6.1
"webpack-manifest-plugin": "^1.2.1" // 1.2.1
}
}
I tried to run the command with Encore.
NODE_ENV=production ./node_modules/.bin/webpack --json > stats.json
but got error
Error: Encore.setOutputPath() cannot be called yet because the runtime environment doesn't appear to be configured. Make sure you're using the encore executable or call Encore.configureRuntimeEnvironment() first if you're purposely not calling Encore directly.
How can I fix this?