为什么 webpack 在使用“import * as _”时不摇树 lodash?

Jon*_*ink 8 javascript lodash webpack es6-modules tree-shaking

我正在使用使用 Lodash 的 webpack 4/React 应用程序学习摇树。

起初,我的 Lodash 用法是这样的:

import * as _ from "lodash";
_.random(...
Run Code Online (Sandbox Code Playgroud)

我很快通过 BundleAnalyzerPlugin 了解到,整个 Lodash 都包含在开发和生产版本中(527MB)。

经过google搜索周围,我意识到,我需要使用特定的语法:

import random from "lodash/random";
random(...
Run Code Online (Sandbox Code Playgroud)

现在,只有random它的依赖项正确地包含在包中,但我仍然有点困惑。

如果我需要在我的import语句中明确指定函数,那么 tree-shaking 实际扮演了什么角色?在比较开发和生产模式构建时,BundleAnalyzerPlugin 没有显示有效负载大小的差异(这两者都是正确的小尺寸,但我认为摇树仅发生在生产构建中?)。

我的印象是 TreeShaking 会执行某种静态代码分析,以确定实际使用了哪些代码部分(可能基于功能?)并剪掉未使用的位。

为什么我们不能总是只*在我们的中使用import并依靠 TreeShaking 来找出实际包含在包中的内容?

如果有帮助,这是我的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")
    }
  }
};
Run Code Online (Sandbox Code Playgroud)

我正在使用webpack --mode=developmentand调用 webpack webpack --mode=production

Aus*_*ras 17

所有两个现有的答案都是错误的,webpack do treeshake import *,但是只有当您使用 esmodule 时才会发生这种情况,而 lodash 则不会。正确的解决方法是使用lodash-es

编辑:这个答案只适用于 webpack4,而 webpack 5 支持 commonjs 的树抖动的有限子集,但我没有自己测试过


Ame*_*icA 6

实际上,这与Webpack的tree-shake能力无关。基于有关tree-shaking的 Webpack 文档

新的 webpack 4 版本扩展了此功能,通过“sideEffects”package.json 属性向编译器提供提示,以指示项目中的哪些文件是“纯”的,因此如果未使用,可以安全地修剪

当您根据链接的文档"sideEffects: false设置时:package.json

上面提到的所有代码都不包含副作用,因此我们可以简单地将属性标记为 false 来通知 webpack 它可以安全地修剪未使用的导出。

如果您有一些您知道它们是纯的文件或包,请将它们添加到其中sideEffects以在未使用时进行修剪。我提供了一些其他解决方案tree-shaking,请阅读 Webpack 文档上的整篇文章

手动方法之一是使用直接导入,如下所示:

import get from 'lodash/get';
Run Code Online (Sandbox Code Playgroud)

Webpack 理解 add 只是从整个 lodash 包中获取。另一种方法是破坏导入,需要一些 Webpack优化来进行树摇动,因此您应该像下面这样导入:

import { get } from 'lodash';
Run Code Online (Sandbox Code Playgroud)

另外,另一种棘手的方法就是安装特定的软件包,我的意思是:

yarn add lodash.get
Run Code Online (Sandbox Code Playgroud)

或者

npm install --save lodash.get
Run Code Online (Sandbox Code Playgroud)

然后导入只需写:

import get from 'lodash.get';
Run Code Online (Sandbox Code Playgroud)

当然,这不是摇树,而是一种紧张的心态发展,但它会让你只添加你想要的东西。

不需要执行上述解决方案的任何操作,您只需通过编写import * as _ from "lodash";然后使用_.random或任何函数来添加整个包,并期望 Webpack 理解您希望发生树摇动?

当然,Webpack 运行良好。您应该使用一些配置和编码风格来查看树摇动的发生。