loading...

JavaScript Module Bundlers

hoangbkit profile image Hoang Nguyen Originally published at advancedweb.dev ・6 min read

Originally posted on advancedweb.dev as part of book JavaScript Tooling.

Module bundlers are tools that process your modern JavaScript applications, internally build dependency graphs which map every modules your projects need and generate one or module bundles.

They're typically different in terms of how many kind of non-JavaScript files (css, json, png, jpeg, xml, etc.) they can process, whether they'll output npm packages or complete applications, and how much custom configurations they support.

They're not general-purpose task runners or built but can be used like those with very limited supported behavior (like webpack focusing on automating frontend tasks and generate a website). They often have but not limited following features:

- Bundle JavaScript code in different module formats
- Bundle HTML, CSS, images, and other file assets
- Target outputs can be packages or complete complications
- Transformed code and assets can be compatible to one or more environments
- Build optimizations like tree-shaking, code-splitting, scope-hoisting, etc.
- Hot module replacement during development
- Custom configuration for advanced use cases
- Hashed output filenames for caching strategy
- Generating source maps and developer friendly logging

They're often released as a CLI tool which you can use to run development server, watch changing files, build production, and serve static. They might be also support npm packages which you can integrate into other workflows.

Webpack

Webpack is the most popular and powerful module bundler for JavaScript when they can process any kind of files and they can output packages, websites, or servers.

They have been well developed with flexible architecture including highly customizable configurations, extensive plugins and loaders ecosystem.

Out of the box, webpack only understands JavaScript and JSON files. Loaders allow webpack to process other types of files and convert them into valid modules that can be consumed by your application and added to the dependency graph.

While loaders are used to transform certain types of modules, plugins can be leveraged to perform a wider range of tasks like bundle optimization, asset management and injection of environment variables.

Because JavaScript can be written for both server and browser, webpack offers multiple deployment targets that can be used in browser-like environments, Node.js-like environment, web workers, or Electron.

Webpack has default support to many out of the box production optimizations that you can't literally do manually like tree shaking, lazy loading, minification, uglified JavaScript, scope hoisting, side-effect-free module pruning.

Webpack now by default supports import and export of any local WebAssembly module. This means that you can also write loaders that allow you to import Rust, C++, C and other WebAssembly host lang files directly.

Those above features has made webpack the number one choice to bundle modern web applications nowadays with little to zero configuration, has transformed from advanced complicated tool to something more user friendly.

Rollup

Rollup is another module bundler that can output both library and application, has default support to ES Modules and famous for tree shaking from early days.

Rollup also has plugin system to customize its behavior like transpiling code before bundling or finding third-party modules in our node_modules folder.

Beside plugins, we can use hooks to affect how a build is run, provide information about a build or modify a build once complete

Rollup takes tree-shaking very seriously as selling point, by supporting ES-Modules by default it has access to static analysis that helps dead code elimination possible, very aggressive on removing unused code and generating small bundles.

We often use Rollup to bundle JavaScript libraries, if we want to use code-splitting or dynamic imports with older browsers, we will need an additional runtime to handle loading missing chunks. We rarely use Rollup to build applications because Webpack is so much better at doing this.

Parcel

Parcel focuses solely on bundling web application with zero-configuration. It has out of the box support for JS, CSS, HTML, file assets with no plugins needed.

While many bundlers require you to install and configure plugins to transform assets, Parcel is a plug-and-play tool which can automatically transform JavaScript using Babel, CSS using PostCSS, HTML using PostHTML, and ven node_modules when needed.

Unlike many other bundlers are fundamentally based around JavaScript assets, Parcel transforms a tree of assets to a tree of bundles. Parcel is quite file-type agnostic, the process of bundling has following phases:

Parcel creates the asset tree by parsing all supported file types, extracting their dependencies and transforming to their final compiled form.

Parcel creates the bundle tree, if an asset is required in more than one bundle, it is hoisted up to the nearest common ancestor in the bundle tree so it is not included more than once.

After the bundle tree is constructed, each bundle is written to a file by a packager specific to the file type. The packagers know how to combine the code from each asset together into the final file that is loaded by a browser.

Browserify

Browserify is a tool for bundling Node packages for the browser, happens to work for browser-based apps pretending to be Node packages.

The module system that browserify uses is the same as node, so packages published to npm that were originally intended for use in node but not browsers will work just fine in the browser too.

Browserify lets you use require in the browser, the same way you'd use it in Node. It's not just syntactic sugar for loading scripts on the client. It's a tool that brings all the resources NPM ecosystem off of the server, and into the client.

Browserify starts at the entry point files that you give it and searches for any require() calls it finds using static analysis of the source code's abstract syntax tree. For every require() call with a string in it, browserify resolves those module strings to file paths and then searches those file paths for require() calls recursively until the entire dependency graph is visited.

Browserify is a build step that you can run before deploying your code, it generates a single bundle file that has everything in it, perhaps the simplest bundler because it uses NodeJS require syntax to import javascript.

Browserify is often integrated in a Gulp workflow because it does not deal with any other assets other than pure javascript modules. You can also bundle your typescript/es6 app with Browserify plugins.

Metro

Metro is a specialized JavaScript bundler for React Native, it takes in an entry file and various options, and gives you back a single JavaScript file that includes all your code and its dependencies.

Metro resolves all modules that are required from the entry point to build a graph, then transforms all modules to formats understandable by target platforms, and finally serializes into one or multiple bundles.

Metro was originally part of react-native project, then moved out separately for better development, aims for fast startup, quick bundling speeds, works with thousands of modules in a single application, and support React Native out of the box.

Summary

Most projects of any scale will have their code split between a number of files, manually including each file with an individual <script> tag is hard and error-prone, you should use module bundlers to automatically bundle everything.

There are many bundlers out there, Rollup is good to bundle packages, Browserify is a wonderful tool for bundling node modules for the browser, Webpack is the preferred tool to bundle anything into web applications, and other less popular ones for limited use cases.

They can work out of the box with many built-in features and support an extensive ecosystem of plugins, loaders, transformers.

They can be used as standalone tools or integrated into a more complicated workflow orchestrated by task runners or build tools.

The primary advantage of a bundler is that it leaves you with far fewer files that the browser has to download. This can give your application a performance advantage, as it may decrease the amount of time it takes to load.

Posted on by:

hoangbkit profile

Hoang Nguyen

@hoangbkit

I turn ☕ into {code} using #javascript

Discussion

markdown guide