6 tips to optimize bundle webpack size for ReactJS application

Tram Ho

Bundle size measurement and graphing

To measure and get an overview of bundle sizes and components, we can use webpack-bundle-analyzer to do this. This tool will generate a fairly detailed and easy to understand chart of the components and their sizes in our bundle.

The easiest way to setup and use is to generate a stats.json file using webpack and turn it on with npx .

webpack-bundle-analyzer will open a browser tab and display a chart like this:

To understand this chart, we need to understand some of the definitions of size here:

  • Stat size is the size of input, after webpack bundle but before optimizers like Minify, Uglifier.
  • Parsed size is the size of the file in memory (after optimization). This is the size of the JavaScript code parsed by the browser.
  • gzip size is the size after compression by gzip (This is the size of the file transmitted over the network).

1, Avoid importing the entire library (global import)

Optimal level: High

With some large libraries, we absolutely can only import the part we need instead of importing the whole library. If done enough and correctly, we can reduce the size of bundle relatively by omitting unused components.

Libraries that can import each part include: lodash , react-bootstrap , antd , …

However, if you do not do enough places, just one place using the wrong import statement will result in our app bundle with the library with unused parts.

An example of Lodash when importing the entire library:

As you can see, lodash was lodash up to 3 times (1 time at lodash.js , once at lodash.min.js and once at partial imports). This would be the worst case scenario we could run into. Imagine with 3-4 libraries like this, how big will our bundle be bloated.

There are two ways to make sure you import each part. Note that this is the way of code, and does not apply to a particular library.

Use the babel plugin

the babel-plugin-transform-imports plugin has the ability to replace the import destructured of an entire library with partial imports.

Config is as follows:

Will have the following effects:

Use ESLint

We can use the no-restricted-imports rule to report an error if we encounter an import statement for the library.

ESLint will issue an error if it gets an import statement like this:

And will pass this sentence

2, Use code-splitting

Optimal level: Depends on each system

By using dynamic import or suspense, we can split our code into async chunks and only load until they are needed. This allows us to reduce the size of the bundle initially, but will not reduce the overall size (which may make the bundle a bit heavier).

Config:

By default, it will create a vendor chunk, which separates the application code from the libraries. This is quite fine when there is an update from the app side, only the app’s code changes, without changing anything to the libraries (only true when the resources are properly cached) the client side will reduce the download time. vendor files.

However, it is necessary to research and consider whether to use it or not, because sometimes code splitting will slow down the user operation because we have to download, parse, and execute more code. Depending on the structure of the system, code splitting may cause the browser to download multiple files at the same time. (With HTTP / 1.1, there is a limit on the number of parallel connections to the same domain – see more )

The recommended way to use that is to divide a chunk by a route. However, this is not required.

Lazy way to load a component:

Here we use dynamic import syntax to tell Webpack to separate a chunk for MyComponent and its dependency co.

Setting webpackChunkName not required, it helps us manage the name of the spit file (see config guide ). If two lazy components have the same name, they will be pushed together into a chunk.

React.lazy is used to allow lazy components to be rendered as a normal component. Suspense provides a fallback component (which will render if the import has not been successful). Suspense can be used to wrap anywhere depending on what you allow the user to see while loading.

Read more about lazy and Suspense here

3, Do not include source map

Optimal level: Depends on each system

Source map is the link between source code and file bundle. It is useful for debugging, but should not appear in production environments.

With JS source-map, the devtool option will handle the generation of source-map.

At development, eval-source-map will help us see the source of the file and speed up the rebuild

At production, we should set it to false to turn off source-map generation

For source-map of CSS, Less or Sass, the configs depend on each loader used. When using css-loader, sass-loader and less-loader, we should set options: { sourceMap: true } and false at production in the loader’s config. The default production is false, so we don’t need to add this setting.

4, Remove replaceable libraries

Optimal level: Depends on each system

Sometimes to handle a request from the spec, we add a new library to handle, but in fact this library can do a lot of other things. Simply because you think that you may need to use it later, or you simply have not thought of how to handle it yet, you can add the library quickly.

Adding a bunch of redundant libraries will greatly affect the bundle file.

Sometimes I even use lodash just to use some of its functions like isEmpty or filter , … Don’t get me wrong, lodash extremely good, but in this case there is really no need to do so.

Rewriting the above functions with pure JS only takes 1-2 hours and results in a reduction of a huge library in bundle.

For each library, we need to analyze:

  • We only use a small part of it, right?
  • Can we rewrite the functions we need in a reasonable amount of time?

If both questions above are YES, then rewriting the necessary functions would be a wiser choice.

5, Eliminate prop-types

Optimal level: High

With React, the prop-types declaration will ensure the data type for the props passed to a component. It is true that they are extremely effective during development, they are also disabled in production environment (for performance reasons). However, the declaration is still in the file bundle.

The Babel transform-react-remove-prop-types plugin will completely remove the prop-type declaration from the bundle files. However, prop-types of dependencies will not be removed.

Note: This plugin should only be enabled for production environments.

6, Aim for modern browsers

Optimal level: Medium

To include polyfills, you have already heard of core-js and regenerator-runtime , right?

By default, all polyfills are included and core-js weigh approximately 154KiB while the regenerator-runtime is only 6.3KiB.

By accepting only modern browsers and their recent versions, we can reduce the size of polyfills.

The Babel-preset-env plugin has the ability to replace the import of all core-js by choosing specific browsers to support.

Config:

To declare supported browsers, we use browserlist syntax

Conclusion

Thank you for reading, if there is any other way or tip, please comment below (bow)

Share the news now

Source : Viblo