How JavaScript works: A deep dive into Webpack

Lawrence Eagles
SessionStack Blog
Published in
9 min readMar 29, 2022

This is post # 58 of the series, dedicated to exploring JavaScript and its building components. In the process of identifying and describing the core elements, we also share some rules of thumb we use when building SessionStack, a JavaScript tool for developers to identify, visualize, and reproduce web app bugs through pixel-perfect session replay.

Introduction

Webpack is a module bundler that packages all our app assets into a production-ready bundle. Webpack does this by bundling all our app’s scripts into one file. And as a result, it minimizes HTTP requests and speeds up the site’s performance.

Also, Webpack helps us to load our app modules and their dependencies and handles different definitions and browser compatibility out of the box.

With the use of loaders, Webpack can process files such as SASS and LESS to CSS and convert JSX and modern ECMAScript into browser-friendly JavaScript. Also, Webpack uses plugins to enable us to hook into its compilation process for advanced operations.

In this article, we will take a deep dive into Webpack. And learn about its feature by elaborating on the points mentioned above using code examples.

We will start by scaffolding a simple app by running the code below:

Next, create a public directory with an index.html file containing the code below:

Also, create an src directory with an index.js file containing the code below:

Now, open the package.json file and edit the scripts section as follows:

Finally, we can run our app by running npm start.

In our small contrived app above, the index.js script uses the Lodash camelCase function to log our greeting to the console. However, when we check the console we get the following error:

Uncaught SyntaxError: Cannot use import statement outside a module as seen below:

This error is thrown because the browser does not know how to resolve the Lodash code, and this is where module bundlers like Webpack can help us out.

To use Webpack, we will install it as a dev dependency by running the following code:

npm i -D webpack webpack-cli

Next, we update the package.json scripts as seen below:

Now, when we run npm run build, Webpack will analyze the code in our index.js file and compile it into a dist/main.js file. And this dist/main.js is our production code. And as a result, we have to update the script src in the index.html file to dist/main.js as seen below:

<script src=”../dist/main.js”></script>

Now, when we run our app everything works correctly and we can see the greeting in the console as seen below:

Now, when we run our app, everything works correctly, and we can see the greeting in the console as seen below:

Modern web development involves multiple third-party modules and utility libraries like Lodash, React, Moment, etc. And following our example above, we can see why we need module bundlers like Webpack.

Also, it is important to note that we used the default settings of Webpack in our example above. But in more complex applications, we will need to customize Webpack’s default behavior. And we can do this via the webpack.config.js file.

Let’s learn about this in the next section.

Customizing Webpack

The webpack.config.js file is a JavaScript module that exports an object that customizes the behavior of Webpack.

The webpack.config.js file has the following properties:

  • entry: defines the entry point to our app. In our example above, the entry point is ./src/index.js which corresponds to the default Webpack setting.
  • In some cases, entry can be an object. And this is particularly useful for code splitting.
  • output: refers to the name of the file that contains our compiled code. By default, output is main.js but we use our custom filename.
  • Note: when determining the file location for output, we can use the Node.js path module to resolve a consistent path name as seen below:
  • mode: this can either be set to “development” which will enable useful tools for development, “production” which enables built-in optimization for production builds, or “none” which simply means no default.

The mode option tells Webpack how to use its built-in optimization settings.

  • module: the module property is used to handle the configuration of our application modules. The module property has a rules property that is an array of objects. And these objects contain the configurations for different modules such as loaders, parser options, etc.

We will learn more about this option in the next section.

Loaders

By default Webpack only understands JavaScript and JSON. Loaders tell Webpack how to handle other file types. There are loaders for nearly everything in Webpack.

For example, to use CSS, we need to use both the style-loader and the css-loader together.

The css-loader loads all the CSS codes into the JS file, and the style-loader adds these styles into the DOM.

To use both loaders, install them as dependencies by running:

npm i style-loader css-loader

Then configure the webpack.config.js file as seen below:

Note: in our above Webpack configuration, we used modern JavaScript syntax. And for this to work correctly, you must add the type: module property to your package.json file.

Now, we can import our CSS file wherever it is required to apply the needed styles.

The benefit of this pattern is seen in cases where we have many different JS components, with each JS component injecting a different component or element into our index.html.

So with this pattern, every time we use one of these components, we can import the CSS we need for only that component instead of loading all of the CSS into the index.html file. And this enables us to modularize our CSS, and import only what we need.

Webpack loaders also enable us to perform transformations. For example, we can use babel-loader to transform our JSX code or latest JavaScript into browser-friendly vanilla JavaScript.

To perform transformation using the babel-loader, we need to install the required dependencies as seen below:

npm i -D babel-loader @babel/core @babel/preset-env

Next, we need to install the presets to use for transformation. E.g, to transform the latest JavaScript, we need to install the @babel/preset-env preset.

Lastly, we would configure our webpack.config.js file as seen below:

Although Webpack loaders can do a lot of the heavy lifting for us, we can tap into the Webpack compiler by using plugins.

We will learn about Webpack plugins in the next section.

Plugins

Webpack plugins enable us to hook into the Webpack compilation lifecycle.

The webpack.config.js file has a plugins property that is an array of different plugins.

To elaborate on this we would use the Webpack HTML Webpack Plugin.

When Webpack bundles our application into a dist folder we still need an index.html file that correctly points to the JS bundle in the dist folder. We can either do this manually or use the plugin mentioned above. Thus this plugin simplifies the generation of HTML files when Webpack creates our bundles. Consequently removing the need for us to supply our own template.

To use this plugin we need to install it as seen below:

`npm i — save-dev html-webpack-plugin`

Now we would configure our webpack.config.js file as seen below:

Note: in our above Webpack configuration, we used modern JavaScript syntax. And for this to work correctly, you must add the type: module property to your package.json file.

Now when our code is bundled, Webpack would generate the following index.html file in the dist folder that points to the path of the output file as seen below:

You can see this file in the dist folder after running npm run build from your CLI.

Conclusion

We have covered a lot in this article yet Webpack is packed with so many features that there are still many things to cover.

Webpack can process images and with the webpack-dev server, we can do things like hot-reloading and more. Webpack is regarded as the swiss army knife of module bundlers.

There are other module bundlers like Parcel and Rollup that still follow the concept of Webpack but try to reduce configurations. An exception, however, is Snowpack which is regarded as the feature because of its innovative unbundled development pattern.

To learn more about these bundlers you can read our previous article on this series.

We all like to apply new technologies & upgrade our code. Even if we feel we’ve tested everything before release it’s always necessary to verify that our users have a great experience with our product.

A solution like SessionStack allows us to replay customer journeys as videos, showing us how our customers actually experience our product. We can quickly determine whether our product is performing according to their expectations or not. In case we see that something is wrong, we can explore all of the technical details from the user’s browser such as the network, debug information, and everything about their environment so that we can easily understand the problem and resolve it. We can co-browse with users, segment them based on their behavior, analyze user journeys, and unlock new growth opportunities for our applications.

There is a free trial if you’d like to give SessionStack a try.

SessionStack replaying a session

If you missed the previous chapters of the series, you can find them here:

--

--