- Technicalpig
- Posts
- TechnicalPig🐷: Understand module bundlers
TechnicalPig🐷: Understand module bundlers
Webpack, Esbuild, What are they?
Bundlers
Bundlers like webpack and esbuild bundle modules together into static assets that are optimised for the web browser.
Most bundlers are static module bundler - this means it bundles as a build step and not dynamically at runtime.
What is a Module? A module is simply a file. In your file e.g. doSomething.js
you will likely have a chunk of code or asset that performs a function. Modern JavaScript supports modules natively (using import
and export
statements), allowing developers to write maintainable and modular code.
Why bundle modules? it is primarily about efficiency. Bundling modules is a process where multiple JS files are compiled and merged into a single file (or a few files) improving performance like load time. Here are the benefits:
Network Efficiency
Fewer requests: Each separate file the browser needs to load is a separate HTTP request. More files means more requests which can slow down page loading. Bundling into less files means less HTTP requests.
Caching: A single bundle file can be cached by the browser which means subsequent page loads will be faster because the browser already has the code stored in the local’s browser cache.
Performance Optimisation
Bundling often includes other optimisation like minification (removing unnecessary code like whitespaces) and tree shaking (remove unused code), making the modules smaller.
Code Splitting: modern webpack allows you to split your code into multiple bundles which can be loaded on demand or in parallel, optimising load time.
Consistency and Compatibility
Bundlers can use tools like Babel to transpile newer JS syntax into a version compatible with old browers, allowing your code to work across wider environments.
Module resolution: While Node.js natively supports CommonJS, web browsers support ES Modules. Bundlers help bridge this gap by resolving modules in a way that they can be correctly imported and used in the browser.
Development Experience
Sourcemaps: Bundlers generate sourcemaps which map the combined and minified code back to the original source files. This helps with debugging - allows you to see where in the original code the error happened.
Hot Module Replacement: changes to the code can be instantly reflected in the browser without needing a full page reload, speeding up development
How bundling works
As explained above, bundling is about efficiency - optimising the loading and execution of web application in a browser and improving the development process.
We will delve into how bundling combines and optimises multiple modules into a smaller number of modules.
Analysis: the bundler starts with an entry point. It then identifies dependencies by looking at
import
statements.Dependency Graph Construction: using this information, it constructs a dependency graph. The graph represents all the modules and how they are interconnected. It arranges the modules in the correct order (a module must be defined because it is used)
Bundling: Using the order defined in the graph, the bundler then merges the code into 1 file (or multiple). It wraps the module code in functions to maintain scope isolation (prevents variables and functions from different modules from clashing in the global scope).
Optimisation: the bundler then executes things like minification, tree shaking, transpiling and polyfills (provide modern functionality on older browsers).
Output: The final step is generating the output files. This includes the bundled file and often includes the sourcemaps.
How does static bundling work with dynamic imports
Modern JS module syntax like ECMAScript allows you to dynamically import modules and runtime.
Dynamic imports use the import()
function to load a module asynchronously. When you use import()
with a module path, it returns a promise that resolves to the module. This lets you conditionally and lazily load modules, components, libraries, etc., at runtime, depending on user actions or other conditions.
Even though static module bundlers run at build time, they are designed to recognise and specifically handle dynamic imports:
Code Splitting: When a bundler encounters a dynamic import, it automatically splits the code at that point, creating a separate bundle (often called a "chunk") for the dynamically imported module. This chunk is loaded only when the
import()
statement is executed at runtime.Loading Mechanism: The bundler generates additional JavaScript code to handle the loading of these chunks at runtime. This includes fetching the chunk, caching it if necessary, and executing it once it's loaded. This process is seamless from a developer's perspective—your code simply uses
import()
, and the bundler takes care of the rest.