The original address

preface

Recently, I was micro-sharing the use of the sideEffects field in Webpack with the team, so I finally put together an article to help more people understand the role of sideEffects.

So what is sideEffects? I can sum it up in one sentence: Let Webpack remove tree shaking code that causes side effects.

It sounds a bit tricky, let’s highlight it: tree shaking/ usage/side effects

tree shaking

Tree Shaking is a code optimization technology that takes away code that is not needed. Here’s a simple example:

// a.js export const a = 'a'; export const b = 'b'; // export const c = 'c'; // index.js import {a, c} from './a.js'; console.log(a); If (false) {// Code that will not execute, delete console.log(' remove me '); }Copy the code

Tree shaking removes unused code during webpack compilation. To summarize:

  1. Tree Shaking is based on ES6 modules, which means that if you reference different files you need to follow the ES6 module specification.
  2. Webpack removes code that is read-only, unwritten, or never executed at compile time.

Method of use

To use sideEffects, your webpack version number must be greater than or equal to 4. If you’re writing a third-party NPM module, sideEffects supports one of the following:

// package.json
{
    "sideEffects": false
}
// antd package.json
{
  "sideEffects": [
    "dist/*",
    "es/**/style/*",
    "lib/**/style/*"
  ]
}
Copy the code

If you want this to work for your business code, you can add it to module.rules, for example:

module.exports = {
  module: {
    rules: [
      {
        test: /\.jsx?$/,
        exclude: /(node_modules|bower_components)/,
        use: {
          loader: 'babel-loader',
        },
        sideEffects: false || []
      }
    ]
  },
}
Copy the code

SideEffects can be written either as false or as an array

  • False To tell Webpack that all file code in this NPM package has no side effects
  • The array tells Webpack that I have no side effects with the specified file code in the NPM package

The sideEffects field is read by webpack during compilation and, if any, it removes any sideEffects references to the package or business code that has sideEffects of its own.

Side effects:

Having said that, what is code with side effects? This is simply a side effect of reading/writing JS references to type attributes, as shown in the following simple example.

var x = {}; Object.defineProperty(x, "a", { get: function(val) { window.x = 'a'; return val; }}); function getA ( x ) { return x.a } getA(x); console.log(window.x); // aCopy the code

X mounts the x field on the window object while obtaining the A property. Just like the property setter, JS references the getter and setter of the property are actually opaque. Webpack, as a conservative, chooses to reserve this kind of code.

This is why WebPack handles ES6 class types badly, and there are a lot of setter operations when Babel escapes the class:

class Person {
  constructor ({ name, age, sex }) {
    this.className = 'Person'
    this.name = name
    this.age = age
    this.sex = sex
  }
  getName () {
    return this.name
  }
}
// 分割线

function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }

var _createClass = function() {
  function defineProperties(target, props) {
    for (var i = 0; i < props.length; i++) {
      var descriptor = props[i];
      descriptor.enumerable = descriptor.enumerable || !1, descriptor.configurable = !0,
      "value" in descriptor && (descriptor.writable = !0), Object.defineProperty(target, descriptor.key, descriptor);
    }
  }
  return function(Constructor, protoProps, staticProps) {
    return protoProps && defineProperties(Constructor.prototype, protoProps), staticProps && defineProperties(Constructor, staticProps),
    Constructor;
  };
}()

var Person = function () {
  function Person(_ref) {
    var name = _ref.name, age = _ref.age, sex = _ref.sex;
    _classCallCheck(this, Person);

    this.className = 'Person';
    this.name = name;
    this.age = age;
    this.sex = sex;
  }

  _createClass(Person, [{
    key: 'getName',
    value: function getName() {
      return this.name;
    }
  }]);
  return Person;
}();
Copy the code

In a simple class, there would be an instant-execute function that performs a set operation on an object property.

The effects of side effects

As a common example, when writing code on a project, we usually distinguish between the development environment and the production environment. For example, during the development phase, some debugging kits are introduced, such as:

import DevTools from 'mobx-react-devtools'; class MyApp extends React.Component { render() { return ( <div> ... { process.env.NODE_ENV === 'production' ? null : <DevTools /> } </div> ); }}Copy the code

At first glance, you might think mobx-react-devtools would not have been introduced in production at all, but mobx-react-devtools was not completely removed without sideEffects, and the sideEffects code would still have been introduced.

Usage Scenarios:

When should it be added? I’m not sure if the NPM module I wrote has any side effects.

We can basically make sure that the package does not affect objects outside of the package, such as whether it changes properties on the window or overwrites the native Array and Object methods. If we can guarantee this, we can actually see if sideEffects: false can be set for the entire package

conclusion

Tree Shaking for Webpack relies on Babel compilation + UglifyJS compression. This process is not complete flow analysis. UglifyJS does not have complete flow analysis. It can simply judge whether a variable is referenced or modified later, but it cannot judge the complete modification process of a variable. We do not know whether it has pointed to external variables, so many codes that may have side effects can only be conservative and not deleted.

Tree Shaking handles code types:

  1. Business code: The ES6 module is packaged with Tree Shaking, and then the code is compressed
  2. NPM package modules: generally available in two versions: a Babel compiled file that provides es6 module mechanism (ps: the Babel compiled configuration of the engineering project, in order to speed up the compilation, it actually ignores the file compiled in node_modules)

Before WebPack4 was released, Tree Shaking did something like this when dealing with third-party NPM modules:

// antd package.json { "name": "my-package", "main": "dist/my-package.umd.js", "module": "dist/my-package.esm.js" } // webpack.config.jss module.exports = { resolve: { mainFields: ['browser', 'module', 'main'], // set main entry},};Copy the code

So webPack will load the ES entry file first when referencing antD components,

// antd entry export Button from './es/ Button '; export Message from './es/message'; export Row from './es/row'; // index.js import { Row } from 'antd';Copy the code

But by doing so, webPack can find the Row entry module and leave the other components (Button, Message, etc.) unpacked, but the code for their side effects is preserved. One way to hack is to replace the module path by introducing babel-plugin-import.

import { Button, Message } form 'antd'; Import Button from 'antd/lib/ Button '; import Message from 'antd/lib/button';Copy the code

In this way, other components that are not referenced are not introduced because the side effects of other components are not mapped through the ANTD main entry component. Of course, there may be some side effect code in the component files you import, and that will be retained.

Now with Webpack4 it’s easy to add sideEffects directly to a third-party module: False, webpack will not be able to hack any files that are not referenced by the es entry, so don’t use babel-plugin-import.

Refer to the link

  • Your tree-shaking is not good for eggs
  • How do I use sideEffects in Webpack?