Why do we use JavaScript bundlers?

Why do we use JavaScript bundlers?

JavaScript is an interpreted language and doesn’t need compilation. Your browser can execute the same code that you write. So why do we use JavaScript bundlers?

Fewer JS files

Historically, the number of JS files used by a website was crucial because of the performance penalty of having many small files. Browsers loaded each file with a separate HTTP request. Every request needed a connection between the browser and server, and those took time to establish. Thanks to HTTP/2, the number of files is much less of an issue now. Still, having files bundled together makes sense. Each request is cached separately, so having plenty of files makes it more challenging to ensure that the browser doesn’t get stale code from the cache.

Besides that, until 2018, many browsers were not supporting ES modules. You were just loading many files from the HTML, and they all shared the same global scope. The JS bundlers address both issues, as they

  • allow you to keep your codebase split into many, well-defined files and
  • bundle the code into big files for deployment.

Image description

Easy import from node_modules

Bundlers provide you with a way of importing dependencies, which is much nicer than loading them as ES modules. To use node packages from the browser, you would need to

  • deploy node_modules to your production server, and
  • use a relative path from your file to the file you want to import

The relative path is a big headache because it forces you to write the import slightly differently depending on how deep in the folder structure you are. So, for using Lodash, you would have:

// in ./src/core.js 
var _ = require('../node_modules/lodash/lodash.js');

// in ./src/app/main.js
var _ = require('../../node_modules/lodash/lodash.js');

The bundlers allow you to write simply:

// anywhere
var _ = require('lodash');

Image description

Import other file types

Your codebase is not only JavaScript. When you organize your code by components or routes, each will come with its own template and styling. Native ES modules don’t let you import resource types other than JS. This limitation would make you import the CSS from the HTML, whereas the rest of the component is imported in JavaScript—thereby forcing you to maintain two unrelated files in sync. JS bundlers fix this problem by letting you manage all those dependencies directly from your JS files:

import ‘./core.js’;
import ‘./style.css’;

const template = require(‘./view.html’);

Image description

Transpile code

A lot of JavaScript is not simple JavaScript; it’s written in languages such as TypeScript and then compiled to JavaScript. This code-to-code compilation is called transpilation. Most of the JavaScript is transpiled for a few reasons.

Code minification

If you’re writing your code as you should, you are doing the following:

  • giving meaningful names to variables
  • indenting the code
  • leaving comments for other developers

This adds clutter that means nothing for the interpreter. Minification is the first step to reducing the payload size. It removes everything that has no impact on your application.

Image description

Downgrade for older browsers

As the language receives new features, there is this period during which

  • developers want to use it already, and
  • not all browsers support it.

Luckily, this period is becoming significantly shorter thanks to the evergreen browser, but there is still a need for a project like Babel. Babel allows you to use the newest language version while coding and transpile it to a version that the older browser will understand.

JavaScript flavors

Besides the plain JavaScript, you can use many of its flavors:

  • TypeScript
  • PureScript
  • Elm
  • CoffeeScript

JavaScript bundlers can handle even mixing of different flavors in one project—which sounds like a bad idea until you end up working with legacy code and need a lot of flexibility to pick the right priorities.

Image description

Separate build for different use cases

Once you start compiling your code with a bundler, new possibilities arise. From the beginning, you will most likely compile the code one way for production and another way for local development. If you write unit tests, maybe you are interested in knowing how well they cover your code. There are code coverage tools that do exactly this. They require a dedicated build that includes tools that count visits to every line of code during the test execution.

How about you?

What JS bundler are you planning to use in your next project? Let me know in the poll, so I know which should get more attention on this blog:

What’s next?

You can check out my article about using native ES modules, or: