Webpack Bundle Analyzer

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.

Start your All-Access Pass
Buy just this tutorial for $12.00

With a Subscription, click any sentence in the script to jump to that part of the video!

Login Subscribe

Each time we add a new Stimulus controller - either by adding a new file to assets/controllers/, or via the controllers.json file when we install a new Symfony UX package - all the code from that controller - and any modules it imports - are added to the set of JavaScript that's included and downloaded on every page. For example, this sweetalert2 code is currently only used on the cart page... but it's downloaded on every page.

But you know how the old saying goes: premature optimization is the root of all evil. Or maybe it's money... or running out of cake...

My point is: this is not necessarily a problem. And the benefit of how this all works is huge! We can write a controller, add a corresponding data-controller element to any page - even loading it via Ajax - and it will work. That is a game changer both for reliability and simplicity.

However, it may not be a bad idea to look at what's inside of our compiled JavaScript files - the stuff that's in the public/build/ directory. And we can easily do that with Webpack.

Dumping a Stats File from Webpack

Find your terminal, go to the tab that has Encore and hit Control+C. Now run:

yarn run --silent build --json > stats.json

I know, that looks a bit funny. The main command in there is yarn run build. That executes the build script that's in our package.json file. So basically, yarn run build is a shortcut to do a production build. We're doing a production build because the point of this command - the --json flag is the magic - is to dump a huge amount of information into a new stats.json file about our built files. And if we're going to optimize our files, it'll be more realistic if we look at a production build.

Installing and Using webpack-bundle-analyzer

But... this file... isn't exactly readable. Fortunately, instead of parsing through this data by hand, we can use a visualization library. Install it with:

yarn add webpack-bundle-analyzer --dev

This adds a new executable to our app. When it finishes, we can run yarn webpack-bundle-analyzer, point it at the stats.json file and then tell it where our built files are located: public/build.

yarn webpack-bundle-analyzer stats.json public/build

Hit it. And... woh! This starts a new, temporary web-server at http://127.0.0.1:8888 and opens our browser. You can see this back on the terminal. If it didn't open a browser for you, just go directly to that URL.

Oh, I can feel the power. This shows every built file and what's inside of it. For example, the biggest file is this 801.js file. The filenames in a production build are purposely short and cryptic. An app.js file is also being output as well as a tiny runtime.js file.

Looking at this... it's pretty obvious that the biggest thing is chart.js. And, though this specific tool doesn't tell us, I know that moment is also here because it's being imported by chart.js.

By the way, as we talked about way back at the start of the tutorial, the reason we see 3 files is that Encore - via Webpack - automatically splits our code into pieces. Head back to the homepage... and view the HTML source. Yep! Our page includes 3 script tags for the 3 files we saw a second ago. These represent the built code for our one Webpack entry called app.

What's important for us is that the contents of these three files are being loaded on every single page.

Can we somehow improve this situation so that our users need to download less JavaScript for every page?

We can! Next, let's learn a native Webpack feature - async imports - that can help us lazily load JavaScript in any file. After that, I'll show you a super amazing trick that's special to Stimulus and Symfony.

Leave a comment!

What PHP libraries does this tutorial use?

// composer.json
{
    "require": {
        "php": ">=7.4.0",
        "ext-ctype": "*",
        "ext-iconv": "*",
        "composer/package-versions-deprecated": "1.11.99.1", // 1.11.99.1
        "doctrine/annotations": "^1.0", // 1.11.1
        "doctrine/doctrine-bundle": "^2.2", // 2.2.3
        "doctrine/doctrine-migrations-bundle": "^3.0", // 3.0.2
        "doctrine/orm": "^2.8", // 2.8.1
        "phpdocumentor/reflection-docblock": "^5.2", // 5.2.2
        "sensio/framework-extra-bundle": "^5.6", // v5.6.1
        "symfony/asset": "5.2.*", // v5.2.3
        "symfony/console": "5.2.*", // v5.2.3
        "symfony/dotenv": "5.2.*", // v5.2.3
        "symfony/flex": "^1.3.1", // v1.12.1
        "symfony/form": "5.2.*", // v5.2.3
        "symfony/framework-bundle": "5.2.*", // v5.2.3
        "symfony/property-access": "5.2.*", // v5.2.3
        "symfony/property-info": "5.2.*", // v5.2.3
        "symfony/proxy-manager-bridge": "5.2.*", // v5.2.3
        "symfony/security-bundle": "5.2.*", // v5.2.3
        "symfony/serializer": "5.2.*", // v5.2.3
        "symfony/twig-bundle": "5.2.*", // v5.2.3
        "symfony/ux-chartjs": "^1.1", // v1.2.0
        "symfony/validator": "5.2.*", // v5.2.3
        "symfony/webpack-encore-bundle": "^1.9", // v1.11.1
        "symfony/yaml": "5.2.*", // v5.2.3
        "twig/extra-bundle": "^2.12|^3.0", // v3.2.1
        "twig/intl-extra": "^3.2", // v3.2.1
        "twig/twig": "^2.12|^3.0" // v3.2.1
    },
    "require-dev": {
        "doctrine/doctrine-fixtures-bundle": "^3.4", // 3.4.0
        "symfony/debug-bundle": "^5.2", // v5.2.3
        "symfony/maker-bundle": "^1.27", // v1.30.0
        "symfony/monolog-bundle": "^3.0", // v3.6.0
        "symfony/stopwatch": "^5.2", // v5.2.3
        "symfony/var-dumper": "^5.2", // v5.2.3
        "symfony/web-profiler-bundle": "^5.2" // v5.2.3
    }
}

What JavaScript libraries does this tutorial use?

// package.json
{
    "devDependencies": {
        "@babel/preset-react": "^7.0.0", // 7.12.13
        "@popperjs/core": "^2.9.1", // 2.9.1
        "@symfony/stimulus-bridge": "^2.0.0", // 2.1.0
        "@symfony/ux-chartjs": "file:vendor/symfony/ux-chartjs/Resources/assets", // 1.1.0
        "@symfony/webpack-encore": "^1.0.0", // 1.0.4
        "bootstrap": "^5.0.0-beta2", // 5.0.0-beta2
        "core-js": "^3.0.0", // 3.8.3
        "jquery": "^3.6.0", // 3.6.0
        "react": "^17.0.1", // 17.0.1
        "react-dom": "^17.0.1", // 17.0.1
        "regenerator-runtime": "^0.13.2", // 0.13.7
        "stimulus": "^2.0.0", // 2.0.0
        "stimulus-autocomplete": "^2.0.1-phylor-6095f2a9", // 2.0.1-phylor-6095f2a9
        "stimulus-use": "^0.24.0-1", // 0.24.0-1
        "sweetalert2": "^10.13.0", // 10.14.0
        "webpack-bundle-analyzer": "^4.4.0", // 4.4.0
        "webpack-notifier": "^1.6.0" // 1.13.0
    }
}