📄 Table of Contents

▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬ ✦ ✦ ✦ ▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬

◉ Webpack

Webpack is an asset bundler that powers an entire React app. In a nutshell, it takes all of the JavaScript, CSS, and any other front-end assets, and produces the output that you see in your browser. It can do advanced things like code splitting to improve the performance of your application, lazy loading, caching, tree shaking, and it helps you create entire libraries that you can distribute across teams.

◉ Code Splitting

Code splitting is one of the most compelling features of webpack. This feature allows you to split your code into various bundles which can then be loaded on demand or in parallel. It can be used to achieve smaller bundles and control resource load prioritization which, if used correctly, can have a major impact on load time.

There are three general approaches to code splitting available:

Entry Points

This is by far the easiest and most intuitive way to split code. However, it is more manual and has some pitfalls we will go over. Let’s take a look at how we might split another module from the main bundle:

project

webpack-demo
|- package.json
|- webpack.config.js
|- /dist
|- /src
|- index.js
+ |- another-module.js
|- /node_modules

another-module.js

import _ from 'lodash';console.log(
_.join(['Another', 'module', 'loaded!'], ' ')
);

webpack.config.js

const path = require('path');module.exports = {
mode: 'development',
entry: {
index: './src/index.js',
+ another: './src/another-module.js',
},
output: {
filename: '[name].bundle.js',
path: path.resolve(__dirname, 'dist'),
},
};

This will yield the following build result:

...
Asset Size Chunks Chunk Names
another.bundle.js 550 KiB another [emitted] another
index.bundle.js 550 KiB index [emitted] index
Entrypoint index = index.bundle.js
Entrypoint another = another.bundle.js
...

As mentioned there are some pitfalls to this approach:

The first of these two points is definitely an issue for our example, as lodash is also imported within ./src/index.js and will thus be duplicated in both bundles. Let's remove this duplication by using the SplitChunksPlugin.

Prevent Duplication

Entry dependencies

The dependOn option allows to share the modules between the chunks

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

optimization.runtimeChunk

optimization.runtimeChunk: 'single' is needed when multiple entry points are being used on a single HTML page.

Using multiple entry points per page should be avoided when possible in favor of an entry point with multiple imports: entry: { page: ['./analytics', './app'] }. This results in a better optimization and consistent execution order when using async script tags.

Multiple entry points per page could be used in scenarios where HTML is generated in a dynamic matter, e. g. when components on page are unknown at compile-time and HTML page is composed dynamically depending on the data.

SplitChunksPlugin

The SplitChunksPlugin allows us to extract common dependencies into an existing entry chunk or an entirely new chunk. Let's use this to de-duplicate the lodash dependency from the previous example:

The CommonsChunkPlugin has been removed in webpack v4 legato. To learn how chunks are treated in the latest version, check out the SplitChunksPlugin.

webpack.config.js

const path = require('path');  module.exports = {
mode: 'development',
entry: {
index: './src/index.js',
another: './src/another-module.js',
},
output: {
filename: '[name].bundle.js',
path: path.resolve(__dirname, 'dist'),
},
+ optimization: {
+ splitChunks: {
+ chunks: 'all',
+ },
+ },
};

With the optimization.splitChunks configuration option in place, we should now see the duplicate dependency removed from our index.bundle.js and another.bundle.js. The plugin should notice that we've separated lodash out to a separate chunk and remove the dead weight from our main bundle. Let's do an npm run build to see if it worked:

...
Asset Size Chunks Chunk Names
another.bundle.js 5.95 KiB another [emitted] another
index.bundle.js 5.89 KiB index [emitted] index
vendors~another~index.bundle.js 547 KiB vendors~another~index [emitted] vendors~another~index
Entrypoint index = vendors~another~index.bundle.js index.bundle.js
Entrypoint another = vendors~another~index.bundle.js another.bundle.js
...

Here are some other useful plugins and loaders provided by the community for splitting code:

Dynamic Imports

Lazy Loading

Lazy, or “on demand”, loading is a great way to optimize your site or application. This practice essentially involves splitting your code at logical breakpoints, and then loading it once the user has done something that requires, or will require, a new block of code. This speeds up the initial load of the application and lightens its overall weight as some blocks may never even be loaded.

One great feature of the web is that we don’t have to make our visitors download the entire app before they can use it. You can think of code splitting as incrementally downloading the app. To accomplish this we’ll use webpack, @babel/plugin-syntax-dynamic-import, and loadable-components.

webpack has built-in support for dynamic imports; however, if you are using Babel (e.g., to compile JSX to JavaScript) then you will need to use the @babel/plugin-syntax-dynamic-import plugin. This is a syntax-only plugin, meaning Babel won’t do any additional transformations. The plugin simply allows Babel to parse dynamic imports so webpack can bundle them as a code split. Your .babelrc should look something like this:

{
"presets": ["@babel/preset-react"],
"plugins": ["@babel/plugin-syntax-dynamic-import"]
}

loadable-components is a library for loading components with dynamic imports. It handles all sorts of edge cases automatically and makes code splitting simple! Here’s an example of how to use loadable-components:

import loadable from "@loadable/component";
import Loading from "./Loading.js";
const LoadableComponent = loadable(() => import("./Dashboard.js"), {
fallback: <Loading />
});
export default class LoadableDashboard extends React.Component {
render() {
return <LoadableComponent />;
}
}

That’s all there is to it! Simply use LoadableDashboard (or whatever you named your component) and it will automatically be loaded and rendered when you use it in your application. The fallback is a placeholder component to show while the real component is loading.

Experience with Front-end Technologies and MERN / MEAN Stack. Working on all Major UI Frameworks like React, Angular.