Bundlers
Maybe a better title for this post would have been "It Isn't Magic, It's Webpack (or Browserify, Brunch or Parcel, etc.)." These are known as module bundlers, which have been defined as packages of code that "compile small pieces of code into something larger and more complex that can run in a web browser." In essence, this will package all of your stylesheets into one package, your TypeScript into another, all of your JavaScript files into yet another, etc.
Module bundlers essentially figure out which bits of code depend on other bits of code (in other words, bundlers identify dependencies) and make sure code runs in the order it needs to. Bundlers ultimately create a dependency graph, starting with a root (which has no dependencies) wherein the further down the graph a bundle of code falls, the more dependencies it has. Further-down code waits its turn for its dependencies to load first before it is loaded itself.
So, What Is Webpack?
Even in simple applications, we write a lot of code in a lot of separate files. We use syntax like @import
and ES6. We use helpers like TypeScript to which allow us to write clearer code and catch errors sooner. Think of applications built with component-based libraries and frameworks like React where we import components or functions that depend on code written somewhere else in our application. The way our code is parsed, packaged, and executed in a way our browsers can understand can seem like nothing short of magic. But it isn't. It's a bundler called Webpack.
What makes Webpack stand out, though, is its ability to gather all dependencies including not just code, but assets such as images, stylesheets (including preprocessors like sass, typescript and more and create the beforementioned dependency graph. And that's the key -- the heart of Webpack is building the dependency graph.
Building the Dependency Graph
The dependency graph consists of a few key components. Here I'll focus on: Entry, Output, Loaders, and Plugins. One can run yarn add webpack webpack-dev-server --save-dev
and create a webpack.config.js
file to your app's root directory to get started. Don't forget to update your package.json
scripts:
"start": "webpack-dev-server",
"build": "webpack"
1. Establish an Entry Point
The first thing Webpack does is establish an entry point. This is going to be the root of the dependency graph. We can do this by creating a variable containing an object which points to the source file. This is typically going to be index.js
. You can create the object with a simple string, but for scalability and the possibility of a need for multiple entry points, let's use an object.
Simple enough.
2. Establish an Output
Once Webpack has finished bundling code and creating the dependency tree, it needs to be told where to put the finished product; This is our Output. Remember, you can name the filename parameter whatever you'd like. Typically this will be named something like main.js
.
Interestingly enough, this is the base information the application needs to bundle code. With this, you can spin up your local server with yarn start
and Webpack will begin doing its thing.
3. Loaders: Bundling File Types Beyond JavaScript
This is where Webpack starts getting so cool.
Here's a screen grab of some imports from the index.js
file of a recent React project of mine called Squad.
It's something we do 100 times a day. But what's really happening behind the scenes? We're telling Webpack about the dependencies index.js
needs. For example, the line import ./index.css
tells Webpack to grab those styles.
In our webpack.config.js
file, we add a module object like the example below (see webpack concepts, loaders).
This object uses a Regular Expression to identify certain file types and then tells Webpack which loader to use before bundling. We're saying, "Webpack compiler when you find a path that ends with .css
in an import, transform/load them using the css-loader
and style-loader
when you bundle."
Important note: "Transforming" means parsing files (other than .js
files), and translating it into something else that Webpack and the browser can understand.
A few examples:
- Have you ever used
@import
or syntax likeurl('./icon.png')
in your CSS files? Webpack's css-loader is the reason why! It parses your.css
file and processes it. That is why you canimport Icon from ./icon.png;
and laterelement.appendChild(Icon)
! As stated in the Webpack Documentation, "The loader will recognize this is a local file, and replace the./icon.png
path with the final path to the image in your output directory. The html-loader handles<img src="./icon.png" />
in the same manner." Isn't that so cool? - TypeScript is a superset of JavaScript that (among other things) allows JS devs to write more statically-typed code to catch errors while writing code. But Webpack doesn't speak TypeScript. The ts-loader parses TypeScript files and converts them into JavaScript Webpack can read.
- What about any ES6 syntax like classes,
let
orconst
variables, arrow functions, default values, destructuring, etc? babel-loader parses the code you wrote, identifies ES6 syntax, and loads and transpiles it to ES5 the browser understands.
You can learn more about how loaders work under the hood, and even how to write your own loaders here.
4. Plugins vs. Loaders
Loaders
- Work on a file level
- Help create the bundle (during or before bundle generation)
Plugins
- Work on a system level
- Affect the created bundle (bundle or chunk level)
- Focus on optimization (uglifyJS takes the
bundle.js
and minimizes it to decrease file size)
The code implementation looks something like this:
The following graphic may help visualize the difference as well:
Summary
Webpack is the culprit behind much of the magical secret sauce that allows us to use syntax and libraries that makes code cleaner, clearer, more scalable. From imports to ES6, developing apps would be difficult without bundlers like Webpack.
You know what's nice... A shiny new mechanical keyboard. Click that if you're interested in a good one. Or click this for another cool option.
Or perhaps you have a good one and you want to spruce it up a bit (you know I did).
Top comments (3)
When I first saw the title of your article, I immediately thought of Arthur C. Clarke's third law:
I can certainly see myself using that quote in the future! I often find myself attempting to draw back the curtain and find out how the magic trick is done.
Great post! Thanks