How to Make Better Optimization With Webpack and SplitChunk in the Production?

Mohammad Taheri
The Startup
Published in
6 min readJan 23, 2021

--

Overview

Hey Guys , I want to share my experience and R&D about how to config better webpack in the production in the react app or angular. Because configuration with webpack is complicated so It’s take alot effort to learn and to use.

what do you learn after reading this story?

In the First I want to set base config for webpack like loaders and entry and output ,But the main purpose is to introduce ways to optimize our bundle to have better loading and caching in the production.

First Step

After creating sample project with npx command , For start webpack , we should pay attention to two mode , development and production , The difference is , In the production we have some config and better optimization for tree shaking and less bundle size , we will check it .

In this story I want say about production mode and development is in my plan in the next story.

Production :

we should set mode in the production but why ?

In the production mode, webpack optimize automatically , and in the following picture we see size of bundle in the production is 164 KB but in the development is 1.24 MB

mode: 'production',
The Difference between production and development mode

Second Step: Source map

The next is Source map ,This option is used to make easier debugging in the production , there are many different source map but for production I use source map for error report ,Furthermore source map is responsible for show main file after we build and help us to find error and other things is help other people to learn from our site :) . In the Webpack document we have this introduction

It adds a reference comment to the bundle so development tools know where to find it.

In the below image I make a mistake in the code , and I want to see what happens and how browser show error in the production mode First I check without source map and I saw this result from browser

Opps ! Where is the Error why I got this ? so because we don’t set devtool and If we got this in the production , In the big Project we should waste time to find error in the app , But with the source-map we can find error in the project like this:

source map webpack

But you may say it has bad affect in the web performance because we are use another file and user should download it ,but it is not , The source map just load when dev tool opened in the browser .

  mode : 'production',
+ devtool: 'source-map',

Third step : set entry , output , plugin

After this step we should set entry and output and loaders for our project but because this is not my hope to explain this and I assume you have basic information about this and if not go to this link

After this , we can have this config for plugins ,

I used MiniCssExtractPlugin ,BundleAnalyzerPlugin , HtmlWebpackPlugin plugins

new BundleAnalyzerPlugin(),
new MiniCssExtractPlugin({
filename: `static/css/[name].[contenthash].css`,
chunkFilename: `static/css/[id].[contenthash].css`
}),
new HtmlWebpackPlugin({
meta: {
viewport: 'width=device-width, initial-scale=1,viewport-fit=cover, shrink-to-fit=no',
'theme-color': '#FF7714',
'apple-mobile-web-app-status-bar-style': '#FF7714',
'og:title': 'Food Delivery',
'og:description': 'A simple Boilerplate of React Js',
'content-type': {'http-equiv': 'content-type', content: 'text/html; charset=UTF-8'}
},
title: 'Food Delivery',
template: 'public/index.html',
minify: {
collapseWhitespace: true,
removeComments: true,
removeRedundantAttributes: true,
removeScriptTypeAttributes: true,
removeStyleLinkTypeAttributes: true,
useShortDoctype: true
}
}),

You can see some extra code in the htmlWebpackPlugin ,because i want to make pwa

theme-color and apple-mobile-web-app-status-bar-style are for pwa and open graph tags for when we want to have a good UX in the sharing our page and other config in the minify property for better minification in the building.

and miniCssExtractPlugin for our styles code .

After this step , It’s time to optimize our production and bundle size :)

Fourth step : Config optimization

In this step we should TerserPlugin for set optimization in our building . Set module Id for choosing algorithm to set id , for example we have named for better debugging and I add hashed for small numeric values

hashed is deprecated in the webpack 5 and you should replace it with deterministic

optimization: {
/**
* for choose module id
* this is deprecated in the webpack 5
*/
moduleIds: 'hashed',

after that , we set runTimeChunk , before config this I should explain some thing about manifest and runtime .

Base Webpack document , We have 3 type of file in the building , one of them is manifest and runtime, In the shortly , we assume ,there are many chunk files in the app but the important question is how they are connect each other ?

Webpack keep all details about chunks and their interaction in the manifest and browser uses this file , But it’s important to extract this from project , for optimize browser caching ,It saves user from re-download files because with any changing in the file ,contentHash changed and manifest changed too.

runtimeChunk: {
name: entrypoint => `runtimechunk~${entrypoint.name}`
},

next level is make minimize to true

minimize: true,

and next step is to add splitChunks , Let me to start with a sample

assume we have two chunk :

chunk A : react-dom,loadash,redux

chunk B : react,react-dom,loadash,some component

You see there are same dependency between two chunks ,but how webpack handle this for building ? it’s seems some dependency duplicated and it has bad effect in the bundle size ,With splitChunks we can create share chunk to not duplicate dependency ,I think it’s good to be this:

chunk A : redux
chunk B : react,some component
vendor-chunk-A-chunk-B : react-dom,loadash

It makes easy to caching but there are some notion we should attension : This vendor behaviour is for dependency ≥ 30kb and for under this threshold webpack duplicated the dependency because cost of request is higher

Of course we can change this threshold with set minSize in the splitchunk

For Testing this option we add another entry and another Htmpwebpackplugin

entry: {
app: [`./src/index.js`],
user: ['./src/user.js']
},
new HtmlWebpackPlugin({
template: `public/index.html`,
favicon: `public/favicon.ico`,
chunks: ["user"],
filename: `user.html`,
})

The chunk property is for filter and just do for this chunk name

you can see this analyze

If you attention you can see lodash is duplicated and it’s not really good honestly , so for create share vendor to add just one lodash we add splitChunks

splitChunks: {
cacheGroups: {
vendor: {
name: "node_vendors",
test: /[\\/]node_modules[\\/]/,
chunks: "all",
}
}
}

The all means , look to all type of chunks ,because we have another type like async and non-async, in the test we choose which module we want .

Of course we can use lazy loading and make it better , and import just that method we want to use

After this it’s good to add TerserPlugin for minimize

new TerserPlugin({
terserOptions: {
format: {
comments: false,
},
},
extractComments: false,
// enable parallel running
parallel: true,
}),

Till here we had good config for production and of course webpack is like a sea and has no limit and you can do any thing with this .

I hope my story helped you 😅 if you have good suggestion you can comment :)

Good Luck 😄

--

--

Mohammad Taheri
The Startup

Front-end Developer , mountain and astronomy lover