Modules and Code Splitting in Webpack 5

By Teknorix on October 30, 2020

Modules

Webpack works with elements called modules. It uses these modules to build a dependency graph.
Modules are sections of code that deal with related functions. It’s always recommended to structure your project according to modular build — this will improve functionality.

Modules in Webpack 5 are grouped according to scripting language used, they can be:

  1. CSS
  2. SASS
  3. LESS
  4. Images
  5. JavaScript
  6. TypeScript

For most of the contents of the webpack build, you will see these items mentioned above.

Every project consists of modules and assets. Assets are basically those files that are not scripted by a developer. Assets can be images and videos.

Always remember to divide your projects into modules. It makes it easier to test and debug. Also, it is a good boost to performance. Writing modular code helps with directory navigation. New developers on the project won’t have to struggle to gel into the team because of this well-written code. Each module has a
defined purpose.

Webpack 5 forms these modules based on how they express the dependencies of modules.

  1. Through a 2015 ECMAScript import statement
  2. Through a CommonJS require() statement
  3. Through an asynchronous module definition (ASM) define and require statement
  4. Through an image URL in a stylesheet
  5. Through an @import statement in a stylesheet

Module Languages and Loaders

If you want webpack to support these modules, you must ensure that you use a programming language that is understood and processed by Webpack.

Webpack does this using the loaders.

Loaders tell Webpack how to process the code that is not naturally understood by Webpack like JavaScript, HTML, or JSON.

There are many loaders for webpack out there; Thanks to the amazing ever-growing Webpack Community.

Once you add the loader for the code you want to be bundled, webpack will then include this processed code in the Bundle. Loaders currently support a large number of languages. Some of the famous ones are:

  1. TypeScript
  2. LESS
  3. SASS
  4. C++
  5. Babel
  6. Bootstrap

Module Resolution

Module resolution takes place with the help of a resolver. Webpack 5 can also use enhance-resolver to resolve paths while bundling. Webpack can resolve three types of file path:

  1. Absolute Path
    example:
    import ‘D:\\Files\\Styles\\PageStyle.less’
  2. Relative Path
    example:
    import ‘../src/components/button’
  3. Module Path
    example:
    import get from ‘lodash/get’

When the path is pointing to a directory (not a specific path) then Webpack will look for package.json’s main field and determines the correct contextual path to use. And if still there is no file present in the directory or if the main field does not return a valid path. Webpack will search for the file specified in resolve. Main configuration (package.json’s main field of application).

Code Splitting

Code splitting allows the user to split the code into various bundles that can be loaded on-demand or in parallel. Code Splitting is the coolest feature in Webpack.

Code splitting has two pros:

  1. Helps achieve smaller bundle
  2. Control the priority of resource loading.

Overall, this will help increase the page load time.

Code Splitting in Webpack 5 can be done using these three approaches:

Entry Point

You specify this in Webpack config.
Example:
I have specified two entry points in the following Webpack config.

Webpack_config

webpack.config.js

When I run npx webpack following two files gets generated.

npx_wordpack_command

Output of npx webpack command

As you can see, entry points are the easiest way to get started with code splitting. But don’t stop here, lets proceed further to see how we can combine this approach with others.

SplitChunksPlugin

This approach uses SplitChunksPlugin which underneath runs a process call dedupe (de-duplication/ prevent duplication) which ultimately splits the code into chunks.

Dedupe is a Python library that uses machine learning to perform matching, deduplication, and entry resolution.

Let us modify our Webpack config to support SplitChunksPlugin:

SplitChunks_config

webpack.config.js

For example purpose, I have used Lodash’s get and isEmpty functions in two separate files.

Lodash's_function

Two entry points both using Lodash’s functions

The output of webpack build is as follows:

Entrypoints_and_vendorbuilds

SplitChunksPlugin separates builds for entry points and vendor builds.

Note that this is a basic approach to split chunks and deduplication. This is a powerful feature that gives you control over the name of the chunk file etc. For more on different types of options available please visit this link.

Another way is to modify the entry component to specify the dependency in the entry itself. (Note here I am not using SplitChunksPlugin)

const path = require('path');

module.exports = {
mode: 'development',
entry: {
index: { import: './src/index.js', dependOn: 'shared' },
another: { import: './src/component/index.js', dependOn: 's
shared: 'lodash',
},
output: {
filename: '[name].bundle.js',
path: path.resolve(__dirname, 'dist'),
},
};

The dependOn options allow sharing modules between the chunks.

Dynamic Imports

This process uses inline functions to make calls to split code within modules.

Dynamic import in simple words means splitting code and optimizing it after a bundle has been built.
You can do this via two approaches:

  1. import() syntax
  2. require.ensure (legacy method)

React Loadable is an excellent example of how dynamic imports work. Truly amazing example. Please click here to read more.
React_Loadable_Example

React Loadable Example

A module can have several chunks it depends on. It totally depends on which code-splitting approach you follow.