Deep Dive into SideEffects Configuration

markliu2013

Mark

Posted on June 4, 2024

Deep Dive into SideEffects Configuration

In Webpack2, support for ES Modules was added, allowing Webpack to analyze unused export content and then tree-shake it away. However, code in the module that has side effects will be retained by Webpack.

For example, there are modules utils/a.js and utils/b.js in the project, and a unified entry is provided through utils/index.js. The module b contains a print statement, which has side effects.

// utils/a.js
export function a() {
   console.log('aaaaaaaaaaaaa');
}

// utils/b.js
console.log('======== b.js ==========');
export function b() {
   console.log('bbbbbbbbbbbbbb');
}

// utils/index.js
export * from './a';
export * from './b';
Enter fullscreen mode Exit fullscreen mode

We add main entry, app.js, which only references the module a. We expect the unused module b to be tree-shaken away.

// app.js
import { a } from './utils';
a();
Enter fullscreen mode Exit fullscreen mode

Let's take a look at the output of webpack build, note that we should use production mode. The results as follows, I have removed the irrelevant webpack startup code.

// output
([
 function(e, t, r) {
   'use strict';
   r.r(t),
     console.log('======== b.js =========='),
     console.log('aaaaaaaaaaaaa');
 },
])
Enter fullscreen mode Exit fullscreen mode

The module b is not included, but the side effect code in b.js is retained, which is reasonable.

What is sideEFfects?

Let's modify the content of b.js.

// utils/b.js
Object.defineProperty(Array.prototype, 'sum', {
   value: function() {
       return this.reduce((sum, num) =sum += num, 0);
   }
})
export function b() {
   console.log([1, 2, 3, 4].sum());
}
Enter fullscreen mode Exit fullscreen mode

We have defined a new method 'sum' on the Array prototype, which has side effects. Then we call this method in the module b, but as the maintainer of the module b, I hope that 'sum' is 'pure', only used by me, and the outside does not depend on its implementation.

Modify package.json, add a new field "sideEffects": false, this field indicates that the entire project is 'side effect free'. Use webpack to compile again, expecting that when the b module is not used, the 'sum' method defined in b will also be tree-shaken off. The result is as follows.

([
 function(e, t, r) {
   'use strict';
   r.r(t), console.log('aaaaaaaaaaaaa');
 },
])
Enter fullscreen mode Exit fullscreen mode

As expected, the entire module b has been tree-shaken off, including the code with side effects.

Therefore, sideEffects can optimize the package size, and to a certain extent, it can reduce the process of webpack analyzing the source code, speeding up the building speed.

You can try the packaging results in situations like referencing the b module, setting the sideEffects value to true, or removing the sideEffects, etc.

sideEffects Configuration

Besides being able to set a boolean value, sideEffects can also be set as an array, passing code files that need to retain side effects (for example: './src/polyfill.js'), or passing wildcard characters for fuzzy matching (for example: 'src/*/.css').

sideEffects: boolean | string[]
Enter fullscreen mode Exit fullscreen mode

Precautions for sideEffects

In real projects, we usually can't simply set "sideEffects" to false, as some side effects need to be retained, such as importing style files.

Webpack will consider all 'import xxx' statements as imports that are not used. If you mistakenly declare them as 'without side effects', they will be tree-shaken away. And since tree-shaking only takes effect in production mode, everything may still seem normal during local development, and problems may not be detected in a timely manner in the production environment.

The following are examples of 'imports that are not used'.

import './normalize.css';
import './polyfill';
import './App.less';
Enter fullscreen mode Exit fullscreen mode

This is import and used.

import icon from './icon.png';
function Icon() {
   return (
       <img src={icon} />
   )
}
Enter fullscreen mode Exit fullscreen mode

For these files with side effects, we need to declare them correctly by modifying the sideEffects value.

// package.json
"sideEffects": [
 "./src/**/*.css"
]
Enter fullscreen mode Exit fullscreen mode

When using it, be sure to set the sideEffects value correctly.

Limitations of sideEffects

The sideEffects configuration is file-based. Once you configure a file to have side effects, even if you only use the part of the file that does not have side effects, the side effects will still be retained.

For example, modify b.js to

Object.defineProperty(Array.prototype, 'sum', {
   value: function() {
       return this.reduce((sum, num) =sum += num, 0);
   }
})
export function b() {
   console.log([1, 2, 3, 4].sum());
}
export function c() {
   console.log('ccccccccccccccccccc');
}
Enter fullscreen mode Exit fullscreen mode

In app.js, only the c method is imported, the b method will be tree-shaken, but the sum method will not be.

Epilogue

sideEffects has a significant impact on the webpack build process, especially for npm modules. It is particularly important to pay attention to the correctness of the declaration when using it.

πŸ’– πŸ’ͺ πŸ™… 🚩
markliu2013
Mark

Posted on June 4, 2024

Join Our Newsletter. No Spam, Only the good stuff.

Sign up to receive the latest update from our blog.

Related