如何最小化 webpack 包的大小?

How to minimize the size of webpack's bundle?

我正在编写一个网络应用程序,使用 reactwebpack 作为我的模块打包器。 到目前为止,我的 jsx 代码真的很轻,整个文件夹的大小是 25 kb。

我从 webpack 创建的 bundle.js 是 2.2 MB。 运行 带有 -p 标志的优化后,它将包减少到 700kb,这仍然非常大。

我查看了 react.min.js 文件,它的大小是 130kb。

是不是webpack生成了这么大的文件还是我做错了什么?

webpack.config.js

var path = require('path');
var webpack = require('webpack');

module.exports = {
  entry: './public/components/main.jsx',
  output: {
    path: __dirname + "/public",
    filename: 'bundle.js'
  },
  module: {
    loaders: [{
      test: /.jsx?$/,
      loader: 'babel-loader',
      exclude: /node_modules/,
      query: {
        presets: ['es2015', 'react']
      }
    }, {
      test: /\.css$/,
      loader: "style!css"
    }]
  }
};

编辑

package.json:

{
  "name": "XChange",
  "version": "0.0.0",
  "private": true,
  "scripts": {
    "start": "node ./bin/www"
  },
  "main": "./bin/www",
  "devDependencies": {
    "body-parser": "~1.13.2",
    "cookie-parser": "~1.3.5",
    "debug": "~2.2.0",
    "express": "~4.13.1",
    "jade": "~1.11.0",
    "morgan": "~1.6.1",
    "serve-favicon": "~2.3.0",
    "react-dom": "~0.14.3",
    "react": "~0.14.3",
    "webpack": "~1.12.9",
    "babel-loader": "~6.2.0",
    "babel-core": "~6.2.1",
    "babel-preset-react": "~6.1.18",
    "babel-preset-es2015": "~6.1.18",
    "react-bootstrap": "~0.28.1",
    "material-ui": "~0.14.0-rc1",
    "history": "~1.13.1",
    "react-router": "~1.0.2",
    "style-loader": "~0.13.0",
    "css-loader": "~0.18.0"
  },
  "dependencies": {
    "express-validator": "~2.18.0",
    "mongoose": "~4.2.9",
    "kerberos": "~0.0.17",
    "bcrypt": "~0.8.5"
  }
}

根据您的评论,您正在使用 material-uireact-bootstrap。这些依赖项由 webpack 与您的 reactreact-dom 包捆绑在一起。任何时候你 requireimport 一个包,它都会被打包为你的包文件的一部分。

这就是我的猜测。您可能正在使用 library 方式导入 react-bootstrapmaterial-ui 组件:

import { Button } from 'react-bootstrap';
import { FlatButton } from 'material-ui';

这很好用,但它不仅捆绑了 ButtonFlatButton(及其依赖项),还捆绑了 整个 库。

缓解它的一种方法是尝试仅 importrequire 需要的东西,比方说 component 方式。使用相同的例子:

import Button from 'react-bootstrap/lib/Button';
import FlatButton from 'material-ui/lib/flat-button';

这只会捆绑 ButtonFlatButton 及其各自的依赖项。但不是整个图书馆。所以我会尝试摆脱你所有的 library 导入并改用 component 方式。

如果您没有使用很多组件,那么它应该会大大减少捆绑文件的大小。

进一步说明:

当您使用 方式导入 all these react-bootstrap and all these material-ui 组件时,无论您实际使用的是哪个。

01/2017 编辑 - 从那以后我对不同的 Webpack 插件有了更多的了解,并想更新它。事实证明 UglifyJS 有一大堆似乎不是很主流的配置选项,但会对您的包大小产生巨大影响。这是我当前的配置,带有一些注释(现场文档很棒):

 new webpack.optimize.UglifyJsPlugin({
      comments: false, // remove comments
      compress: {
        unused: true,
        dead_code: true, // big one--strip code that will never execute
        warnings: false, // good for prod apps so users can't peek behind curtain
        drop_debugger: true,
        conditionals: true,
        evaluate: true,
        drop_console: true, // strips console statements
        sequences: true,
        booleans: true,
      }
    })

我曾经遇到过一个关于转义 unicode 字符的 uglify 化的晦涩问题,所以如果您使用这些转换,那么请注意这样的边缘情况是可能的。

您可以在 webpack docs 中阅读有关 webpack 支持的特定选项的更多信息,以及一些后续链接以进一步阅读。


(旁注:我认为您的 package.json 是混淆的...至少其中一些开发依赖项是我所见过的每个 package.json 中的依赖项(例如 react-starter-kit)

如果您正在为生产做准备,您还应该采取几个步骤来减小文件大小。这是我的 webpack.config.js 的片段:

 plugins: [


        new webpack.optimize.UglifyJsPlugin(),
        new webpack.optimize.DedupePlugin(),
        new webpack.DefinePlugin({
            'process.env': {
                'NODE_ENV': JSON.stringify('production')
            }
        })
    ],

1) minifies/uglifies 你的代码

2) 替换重复代码以最小化文件大小

3) 告诉 webpack 省略它用于节点环境构建的一些东西

最后,如果您使用源映射(您可能应该使用),您需要添加适当的行。哨兵写道 a nice blog post about this

在我的构建中,我使用开发工具:'source-map' 用于生产

你有没有看过你的脚本是如何通过网络发送的...我有一些非常简单的反应组件,每个组件大约 300kb,那是在 webpack 优化之后。 在它们被 gzip 压缩后,它们下降到 38kb。仍然相当大 - 但这就是我们今天使用明天的功能所得到的。 如果您使用 node/express 来提供静态资源,包括您的 javascript - 请查看压缩 (https://github.com/expressjs/compression)。 我还建议查看生产节点最佳实践指南 https://expressjs.com/en/advanced/best-practice-performance.html 如果您不通过节点提供文件,那么 apache(或其他网络服务器)将有用于压缩基于文本的文件的选项。

更新 05/18 : 更新 UglifyJsPlugin 设置以获得更好的缩小

我在生产代码中使用以下配置进行缩小。

 plugins: [
    new webpack.DefinePlugin({
      'process.env': {
        // This has effect on the react lib size
        'NODE_ENV': JSON.stringify('production'),
      }
    }),
    new ExtractTextPlugin("bundle.css", {allChunks: false}),
    new webpack.optimize.AggressiveMergingPlugin(),
    new webpack.optimize.OccurrenceOrderPlugin(),
    new webpack.optimize.DedupePlugin(),
    new webpack.optimize.UglifyJsPlugin({
      mangle: true,
      compress: {
        warnings: false, // Suppress uglification warnings
        pure_getters: true,
        unsafe: true,
        unsafe_comps: true,
        screw_ie8: true,
        conditionals: true,
        unused: true,
        comparisons: true,
        sequences: true,
        dead_code: true,
        evaluate: true,
        if_return: true,
        join_vars: true
      },
      output: {
        comments: false,
      },
      exclude: [/\.min\.js$/gi] // skip pre-minified libs
    }),
    new webpack.IgnorePlugin(/^\.\/locale$/, [/moment$/]), 
    new CompressionPlugin({
      asset: "[path].gz[query]",
      algorithm: "gzip",
      test: /\.js$|\.css$|\.html$/,
      threshold: 10240,
      minRatio: 0
    })
  ],

我发现提及 source-map-explorer 实用程序很有用,它有助于了解 bundle.js 文件中的确切内容。它可以帮助您识别 bundle js 中是否有任何不必要的东西。 你可以从 npm 安装 source-map-explorer 并像

一样使用它
source-map-explorer yourBundle.js

除此之外,如@kimmiju 所述,请检查您的服务器是否正在使用一些压缩。

你也可以尝试asynchronously load routes(webpack中的延迟加载),这样你的整个bundlejs文件就不会一次性发送,而是分块发送用户导航到这些路线。