icon / menu / white V2Created with Sketch.
Switch LanguageSwitch Language
Dissecting Webpack: Part II

Dissecting Webpack: Part II

In the previous post, we had discussed the setting up of Webpack configuration file and kick-starting it with Webpack development server.

But what makes Webpack a one-stop bundler are the loaders and plugins.

Loaders help Webpack to transform code or help in the development aspect whereas plugins come in at the end when the bundling is happening to enhance or optimise the performance of the application. The following figure shows some recommended loaders and plugins.

web development, webpack configuration chart

As some of you may have noticed in the configuration chart we have above, there are 2 attributes that have not been mentioned yet:

module: {rules: []},
plugins: []

The loaders configuration goes into modules.rules array while plugins configuration goes into…plugins array. So that was easy. Let’s dive right into, firstly, loaders.

Loaders

Babel Loader

npm install babel-loader --save-dev

This loader transpiles >ES5 code to ES5. The configuration is as follows: 

This informs Webpack to use babel-loader only on jsx files and exclude looking into node modules.

Along with the loader, there are a bunch of dependencies to be installed:

npm install babel-core --save-dev

One may ask why we need a babel-loader when we already have a babel-polyfill. To understand this, we need to see what is the function of each tool. This video has a good explanation on the difference. In short, babel-loader takes care of transforming syntax that is above ES5, and the babel-polyfill is called to create new functions and methods, that browsers don’t support, on the fly. Both complement each other and are needed to handle different parts of the modern JS. For example, ‘const’ is transpiled to ‘var’ & and arrow function will be transpiled to an anonymous function.

const myArray = new BetterArray( [1, 2, 3] )
→ var myArray = new BetterArray( [1, 2, 3] )
var nums = list.map( ( v, i ) => v + i )
→ var nums = list.map( function(v, i) {
return v + i
} );

CSS Loader & Style Loader

Since Webpack only understands Javascript, we need to add loaders to tell it how to handle CSS files.

npm install css-loader --save-dev

CSS loader will look into all the css imports and urls and return a string (as shown below) that will be part of the main js.

... \"body {\\n background-color: pink;\\n\"...

Since it is the part of the JS file, the browser does not have the capability to recognise and extract the css code from the js, so the styles will not be applied.

npm install style-loader --save-dev

What we need is a style loader that extracts the css string out of the js bundle and inject it into styles tag of the html file.

With the above configuration, which specifies to use style and css loaders (in that order shown above) on all files, excluding ones in node modules, with a .css extension, we have the styles applied and CSS taken care of.

File Loader & URL Loader

Another set of assets that we need to explicitly tell Webpack how to handle is images and fonts. How we usually manage images is either to inject them inline in the <img> tags or store them in a server to make network requests to render the images.

npm install file-loader --save-dev

File loader only alters the path name of the file to give it a public URL. It looks for all the imports and urls of images being used and formats the path name accordingly. 

With the above configuration, the file-loader will load all the assets with the specified extensions, and place the images in the ‘/images’ folder with the format [name]_[hash].[ext]. Below are a couple of examples:

background-image: url('images/dog.png')
→ images/dog_436bd585...png
import Cat from 'images/cat.jpg'
→ images/cat_875ds32132dsda3...jpg

However, if we place all the images on a server, the overheads for making multiple network request could dampen the performance. There is an advanced loader that is a wrapper around the file-loader, which is known as url-loader.

npm install url-loader --save-dev

What this loader does is handle images based on their size.

This configuration looks a lot like the one for file-loader, but the interesting part is the attribute ‘limit’. This states the size limit of the image.

< 8kb
? <img src='background-image: url(data:image/png;base64,iGwfd..)' />
: images/dog_876bd585bdc8a5cc40633ffefdb7a4a5.png

If, in this case an image is less than 8kb, the url-loader will convert the image to base 64 string and inject it to the <img> tag, else it will default to file-loader which will create a public URL to be saved in the server. Images up to a certain size will be converted to base64 without slowing down the application. The limit can be toggled to gauge the most optimised performance for the project.

Standard Loader

This is an optional loader to help in development. This loader lints code based on Standard JS rules.

This loads as a pre-loader and lints all .jsx files, excluding ones in node modules.

In 4.0
For using loaders in Webpack 4.0 you still have to create a configuration file, and most of the configurations remains.

Plugins

HTML Webpack Plugin

This plugin helps in the creation of a html file from scratch and injects variables into the HTML file.

npm install html-webpack-plugin --save-dev

Following is an example of what variables to set.

These variables can be used in the index.html file such as:

<title><%= htmlWebpackPlugin.options.title %></title>
<link rel="icon" href="<%= htmlWebpackPlugin.options.favicon%>">
<div id="<%= htmlWebpackPlugin.options.appMountId%>"></div>

Extract Text Webpack Plugin

This plugin allows to extract text of any kind to a separate file.This configuration, uses CSS loader to resolve all the CSS but instead of injecting them into the style tags, with the help of style-loader, the plugin will take the CSS string and push it into another file. The extracted file will be rendered in parallel to the bundled file. Doing this has its pros and cons. Let’s go through them.

Pros are that having the CSS in a separate file and not in style tags, will obviously reduce the style tags used and consequently the bundle size. With smaller bundle size, the load time will be faster. As mentioned earlier the CSS file renders in parallel which eliminates the flash of unstyled content.

However, enabling this extraction will stop hot reload from working. And since we have another file, depending on the size, the compilation time will be longer.

It is recommended to have this plugin only for production mode.

In 4.0
Extract-text-webpack-plugin is deprecated and is replaced by mini-css-extract-plugin.

Common Chunks Plugin

Another very important aspect of bundling the project is code splitting. This can substantially optimise the performance of the application. This plugin helps to split out common codes in the project into chunks. These chunks can be loaded on demand or in parallel. This aims to achieve smaller bundle size and loading prioritisation.

web development, common chunks plugin

Credit: Roohiya Dudukela

A small js file can grow to be really big which does not bode well for any application. So, it can be chunked into, for example,

  • Multiple entries
  • Chunks with common codes within multiple entries (Lodash)
  • Vendor libraries that do not change as frequently as the main codebase (React)
 

In the above example, we are adding another entry to the Webpack bundle, ‘vendor’. This is chunking out 3rd party libraries.

With this separate chunk, our main chunk will be reduced considerably!

In 4.0
CommonsChunkPlugin has been deprecated and instead the APIs optimize.splitChunks & optimization.runtimeChunk can be used. This is possible with the new plugin, SplitChunksPlugin. Instead of manually specifying what to chunk, the plugin is smart enough to identify modules that need to be chunked.

UglifyJS Webpack Plugin

This is a familiar plugin which obfuscates code and handles dead code elimination.

 

Mode

Webpack.common.js

A common Webpack simply consists of configurations that are common between dev and production:

  • Babel-polyfill
  • Babel-loader
  • CSS & Style loader
  • URL loader

So the configuration for Webpack.common.js is:

Webpack.dev.js

  • Development Server with hot reload
  • Standard loader
  • CSS loader with sourcemap (for debugging purposes)

The configuration for Webpack is:

Webpack.prod.js

  • CSS extraction
  • Uglify & dead code elimination
  • Code splitting

And the configuration for Webpack.prod.js is:

Webpack Merge

We can combine the common and dev, also the common and prod with Webpack-merge.

In 4.0
Without the need of Webpack merge, we can use the script configuration to specify the -- mode flag and Webpack will take care of the rest.
"dev": "webpack — mode development",
"build": "webpack — mode production"
For a complete react-redux webpack configuration, please take a look at this Github Repo for guidance.

So hopefully this has been an enlightening journey, making you feel more under control over Webpack configurations. No more running away from this!

References

4 Key Concepts of Webpack | Netlify
Webpack is JavaScript module bundler that has taken the world by storm, but a lack of great docs and wealth of…www.netlify.com

Extract Text Plugin
In the last lesson, we got our styles working all good; getting the css and scss files bundled and then getting the…medium.com

Plugins
Installation Getting Started Asset Management Output Management Development Hot Module Replacement Tree Shaking…webpack.js.org

webpack-contrib/file-loader
file-loader — A file loader for webpackgithub.com

DevServer
Installation Getting Started Asset Management Output Management Development Hot Module Replacement Tree Shaking…webpack.js.org

Webpack 4 Tutorial: from 0 Conf to Production Mode (Updated)
webpack 4 is out! The popular module bundler gets a massive update. webpack 4, what’s new? A massive performance…www.valentinog.com

Related articles

Supercharging Strapi Development with A Game-Changing Starter Repository
2 mins
Developer toolbox
Supercharging Strapi Development with A Game-Changing Starter Repository
Accelerating DevOps with DORA Metrics: A Data-Driven Approach
4 mins
Developer toolbox
Accelerating DevOps with DORA Metrics: A Data-Driven Approach
From Ideation to Deployment: Gen AI in the Software Development Life Cycle
3 mins
Developer toolbox
From Ideation to Deployment: Gen AI in the Software Development Life Cycle

Button / CloseCreated with Sketch.