.ArticleCopy img border: solid 1px gray !important;
Nowadays, we’re forced to use many accessory tools to facilitate, speed up and optimize our web development workflow. Often, though, such tools add an extra layer of complexity into the stack. As a result, we need to utilize additional time and effort to learn, understand and use these tools correctly. The same is true for webpack.
When using webpack for the first time, it can be difficult to understand how it works and how it should be used. Although it has good documentation, it can be daunting for novices, and it has a steep learning curve. However, webpack is worth learning and can save considerable time and effort in the long run. In this tutorial, I’ll introduce all the core concepts to help you get started.
Note: in this tutorial I’ve used webpack 5.9.0.
What Is Webpack?
Now, to expand the above cursory overview, let’s explore the main concepts webpack uses.
Webpack Main Concepts
Webpack has some main concepts which we need to understand clearly before digging in its practical implementation. Let’s examine them one by one:
- Entry: the entry point is the module that webpack uses to start building its internal dependency graph. From there, it determines which other modules and libraries that entry point depends on (directly and indirectly) and includes them in the graph until no dependency is left. By default, the entry property is set to
./src/index.js, but we can specify a different module (or even multiple modules) in the webpack configuration file.
- Output: the output property instructs webpack where to emit the bundle(s) and what name to use for the file(s). The default value for this property is
./dist/main.jsfor the main bundle and
./distfor other generated files — such as images, for example. Of course, we can specify different values in the configuration depending on our needs.
- Plugins: plugins are used for any other task that loaders can’t do. They provide us with a wide range of solutions about asset management, bundle minimization and optimization, and so on.
- Mode: typically, when we develop our application we work with two types of source code — one for the development build and one for the production build. Webpack allows us to set which one we want to be produced by changing the mode parameter to development, production or none. This allows webpack to use built-in optimizations corresponding to each environment. The default value is production. The none mode means that no default optimization options will be used. To learn more about the options webpack uses in development and production mode, visit the mode configuration page.
How Webpack Works
index.html file with the appropriate CSS and JS links, and the necessary assets. Also, if you have many CSS and JS modules which depend on each other, they need to be optimized and properly combined in one unit ready for production.
To do all this, webpack relies on configuration. Starting from version 4 and above, webpack provides reasonable defaults out of the box, so creating a configuration file is not required. However, for any non-trivial project you’ll need to provide a special
webpack.config.js file, which describes how the files and assets should be transformed and what kind of output should be generated. This file can quickly become monolithic, which makes it hard to understand how webpack does its job unless you know the main concepts behind its working.
Based on the provided configuration, webpack starts from the entry points and resolves each module it encounters while constructing the dependency graph. If a module contains dependencies, the process is performed recursively against each dependency until the traversal has completed. Then webpack bundles all the project’s modules into a small number of bundles — usually, just one — to be loaded by the browser.
What’s New in Webpack 5
A webpack 5 release was announced in October 2020. The post is quite long and explores all the changes made to webpack. It’s impossible to mention all changes and it’s unnecessary for a beginner’s guide like this. Instead, I’ll try to put a small list with some general highlights:
- The build performance is improved with Persistent Caching. Developers can now enable a file-system–based cache, which will speed up the development builds.
- The Long Term Caching is also improved. In webpack 5, changes made to the code that don’t affect the minimized bundle version (comments, variable names) won’t result in cache invalidation. Also, new algorithms were added which assign short numeric IDs to modules and chunks and short names to exports in a deterministic way. In webpack 5, they’re enabled by default in production mode.
- Improved bundle size, thanks to better Tree Shaking and Code Generation. Thanks to the new Nested Tree-Shaking feature, webpack is now able to track access to nested properties of exports. The CommonJs Tree Shaking allows us to eliminate unused CommonJs exports.
- The minimum supported Node.js version has increased from 6 to 10.13.0 (LTS).
- The codebase is cleaned up. All items marked as deprecated in webpack 4 are removed.
- Automatic Node.js polyfills are removed. Previous versions of webpack have included polyfills for native Node.js libraries like
crypto. In many cases they are unnecessary and increase the bundle size drastically. That’s why webpack 5 stops automatically polyfilling these core modules and focuses on front-end–compatible modules.
- As an improvement of development, webpack 5 allows us to pass a list of targets and also support versions of target. It provides automatic determination of the public path. And also, it offers automatic, unique naming, which prevents conflicts between multiple webpack runtimes that use the same global variable for chunk loading.
webpack-dev-servercommand is now
- Asset modules are introduced, which replace the uses of
Please open the announcement link above to find more complete and detailed information about all the updates.
Finally, if you’re coming from webpack 4, here’s the migration guide.
Note: you can find the files for our project in the GitHub repo.
Now that we have a solid theoretical foundation, let’s implement it in practice.
To start, we’ll create a new directory and switch to it. Then we’ll initialize a new project:
mkdir learn-webpack cd learn-webpack npm init -y
Next, we need to install webpack and webpack CLI (command line interface) locally:
npm install webpack webpack-cli --save-dev
Now, the content of the generated
package.json should be similar to the following:
"name": "learn-webpack", "version": "1.0.0", "description": "", "main": "index.js", "scripts": "test": "echo "Error: no test specified" && exit 1" , "keywords": , "author": "", "license": "ISC", "devDependencies": "webpack": "^5.9.0", "webpack-cli": "^4.2.0"
Besides being a package manager,
npm can be used as a simple task runner. We can create webpack tasks by including the name of our task followed by its instructions in the
scripts section of the
package.json file. Let’s try this now. Open
package.json and change the
scripts object to the following:
"scripts": "test": "echo "Error: no test specified" && exit 1", "dev": "webpack --mode development", "build": "webpack --mode production" ,
npm allows us to reference locally installed Node.js packages by their names. We use that and the
--mode flag to define
build tasks, which will run webpack in development (
npm run dev) and production (
npm run build) mode respectively.
Before we test the tasks we’ve just created, let’s create a
src directory and put an
index.js file in it so that it contains
console.log("Hello, Webpack!");. Now we can already run the
dev task to start webpack in development mode:
$ npm run dev > email@example.com dev C:WEBDEVlearn-webpack > webpack --mode development [webpack-cli] Compilation finished asset main.js 874 bytes [emitted] (name: main) ./src/index.js 31 bytes [built] [code generated] webpack 5.9.0 compiled successfully in 122 ms
As I mentioned before, webpack sets the default entry point to
./src/index.js and the default output to
./dist/main.js. So what webpack does when we run the
dev task is to get the source code from
index.js file and bundle the final code in a
Great! It works as expected. But to verify that we get the correct output, we need to display the result in the browser. To do that, let’s create an
index.html file in the
<!doctype html> <html> <head> <title>Getting Started With Webpack</title> </head> <body> <script src="http://www.sitepoint.com/main.js"></script> </body> </html>
Now, if we open the file in the browser, we should see the Hello, Webpack! message in the console.
So far, so good. But writing our
index.html file manually can be problematic in some cases. For example, if we change the name of our entry point, the generated bundle will be renamed, but our
index.html file will still reference the old name. So, we’ll need to update our HTML file manually every time we rename an entry point or add new one. Fortunately, we can easily fix that with the
html-webpack-plugin. Let’s install it now:
npm install html-webpack-plugin@next --save-dev
Note: notice that I have typed
html-webpack-plugin@next instead of just
html-webpack-plugin. At the time of writing, the former is the proper version for webpack 5, and the latter is the version for webpack 4. This could change in future, so for the actual version check the html-webpack-plugin repo.
At this point, to activate the plugin, we need to create a
webpack.config.js file in the root directory with the following content:
const HtmlWebpackPlugin = require("html-webpack-plugin"); const path = require('path'); module.exports = plugins: [ new HtmlWebpackPlugin( title: "Webpack Output", ), ], ;
As you can see, to activate a webpack plugin, we need to include it in the file and then add it to the
plugins array. If needed, we also pass options to the plugin. See the
html-webpack-plugin repo for all available options and the ability to write and use your own templates.
Let’s run webpack now to see what will happen:
$ npm run dev > firstname.lastname@example.org dev C:WEBDEVlearn-webpack > webpack --mode development [webpack-cli] Compilation finished asset main.js 874 bytes [compared for emit] (name: main) asset index.html 234 bytes [emitted] ./src/index.js 31 bytes [built] [code generated] webpack 5.9.0 compiled successfully in 151 ms
Let’s open the
index.html. As we can see, the plugin automatically creates an updated
index.html file for us, which uses the
title option from the configuration:
<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title>Webpack Output</title> <meta name="viewport" content="width=device-width, initial-scale=1"> <script defer src="http://www.sitepoint.com/main.js"></script> </head> <body> </body> </html>
Let’s now expand our project and specify custom names for the
output properties. In
webpack.config.js we add the following before the
entry: main: path.resolve(__dirname, './src/app.js'), , output: filename: '[name].bundle.js', path: path.resolve(__dirname, 'deploy') ,
Here, we change the entry file to
app.js and the output folder to
deploy. We also tweak the name of the generated bundle file slightly. Now it will start with the name of the entry (“main”) followed by the word “bundle” and the
.js file extension.
Now, we’ll create an
export default (text = "Hello, Webpack!") => const element = document.createElement("h1"); element.innerHTML = text; return element; ;
Next, we rename
app.js to reflect our changes, and replace its content with the following:
import component from './component'; document.body.appendChild(component());
Now, let’s run webpack again:
$ npm run dev > email@example.com dev C:WEBDEVlearn-webpack > webpack --mode development [webpack-cli] Compilation finished asset main.bundle.js 4.67 KiB [emitted] (name: main) asset index.html 241 bytes [emitted] runtime modules 668 bytes 3 modules cacheable modules 230 bytes ./src/app.js 79 bytes [built] [code generated] ./src/component.js 151 bytes [built] [code generated] webpack 5.9.0 compiled successfully in 194 ms
Let’s examine and clarify the information from the webpack output. After the “Compilation finished” message you can see the files generated in the
deploy directory (
index.html). Below them, you can see the source files: the entry module (
app.js) and its dependency (
So now, in the
deploy folder, we have the newly generated bundle file
main.bundle.js. If we open the
index.html file in the browser, we should see Hello, Webpack! displayed on the page.
Also, if we check the source of
index.html, we’ll see that the value of the
src property in the
script tag is updated to
At this point, we can delete the
dist folder, which webpack generated initially, because we won’t need it anymore.
A Beginner’s Guide to Webpack