Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why webpack doesn't tree-shake the lodash when using "import * as _"?

I am learning about tree-shaking with a webpack 4/React application that uses Lodash.

At first, my Lodash usage looked like this:

import * as _ from "lodash";
_.random(...

I soon learned, via the BundleAnalyzerPlugin, that the entirety of Lodash was being included in both dev and prod builds (527MB).

After googling around I realized that I needed to use a specific syntax:

import random from "lodash/random";
random(...

Now, only random and it's dependencies are correctly included in the bundle, but I'm still a little confused.

If I need to explicitly specify functions in my import statement, then what role is the tree-shaking actually playing? The BundleAnalyzerPlugin isn't showing a difference in payload size when comparing between dev and production mode builds (it's the correct small size in both, but I thought that tree-shaking only took place with production builds?).

I was under the impression that TreeShaking would perform some sort of static code analysis to determine which parts of the code were actually being used (perhaps based on function?) and clip off the unused bits.

Why can't we always just use * in our import and rely on TreeShaking to figure out what to actually include in the bundle?

In case it helps, here is my webpack.config.js:

const path = require("path");
const BundleAnalyzerPlugin = require("webpack-bundle-analyzer").BundleAnalyzerPlugin;

module.exports = {
  entry: {
    app: ["babel-polyfill", "./src/index.js"]
  },
  plugins: [
    new BundleAnalyzerPlugin({
      analyzerMode: "static",
      openAnalyzer: false
    })
  ],
  devtool: "source-map",
  output: {
    filename: "[name].js",
    path: path.resolve(__dirname, "dist"),
    chunkFilename: "[name].bundle.js",
    publicPath: ""
  },
  module: {
    rules: [
      {
        test: /\.js$/,
        loader: "babel-loader",
        include: /src/,
        options: {
          babelrc: false,
          presets: [
            [
              "env",
              {
                targets: {
                  browsers: ["last 2 Chrome versions"]
                }
              }
            ],
            "@babel/preset-env",
            "@babel/preset-react"
          ],
          plugins: ["syntax-dynamic-import"]
        }
      },
      {
        test: /\.(ts|tsx)$/,
        use: [
          {
            loader: require.resolve("ts-loader"),
            options: {
              compiler: require.resolve("typescript")
            }
          }
        ]
      }
    ]
  },
  resolve: {
    symlinks: false,
    extensions: [".js", ".ts", ".tsx"],
    alias: {
      react: path.resolve("./node_modules/react")
    }
  }
};

I'm invoking webpack with webpack --mode=development and webpack --mode=production.

like image 474
Jonathan.Brink Avatar asked Nov 07 '19 02:11

Jonathan.Brink


People also ask

Why is webpack not tree shaking?

In webpack, tree shaking works with both ECMAScript modules (ESM) and CommonJS, but it does not work with Asynchronous Module Definition (AMD) or Universal Module Definition (UMD).

How do you import lodash from shaking trees?

Tree-shaking is syntax-sensitive. To enable tree-shaking in lodash, you have to import functions using syntax like import foo from 'lodash/foo' . import { foo } from 'lodash' will not tree-shake, nor will, obviously, import _ from 'lodash' . Support for this syntax was implemented in Lodash v4.

How does tree shake work in webpack?

Tree shaking is a term commonly used in the JavaScript context for dead-code elimination. It relies on the static structure of ES2015 module syntax, i.e. import and export . The name and concept have been popularized by the ES2015 module bundler rollup.

Does webpack tree shake by default?

Basic Webpack ConfigurationWebpack only does tree shaking when doing minification, which will only occur in production model. Second, you must set the optimization option “usedExports” to true. This means that Webpack will identify any code it thinks isn't being used and mark it during the initial bundling step.


1 Answers

All two existing answers are wrong, webpack do treeshake import *, however that only happens when you're using a esmodule, while lodash is not. The correct solution is to use lodash-es

Edit: this answer only applies to webpack4, while webpack 5 supported a limited subset of tree shaking for commonjs, but I haven't tested it myself

like image 150
Austaras Avatar answered Sep 21 '22 23:09

Austaras