4
votes

I find my webpack vendor bundles very large (~1.6 MB originally). After using webpack-bundle-size-analyzer, I found angular to be contributing a large part to this.

➜  dapper-ui git:(master) webpack --config ./conf/webpack-dist.conf.js --json | webpack-bundle-size-analyzer
angular: 1.13 MB (25.9%)
lodash: 526.94 KB (11.8%)
moment: 429.12 KB (9.58%)
angular-ui-router: 339.35 KB (7.58%)
chart.js: 311.27 KB (6.95%)
angular-ui-bootstrap: 264.26 KB (5.90%)
ng-table: 197.55 KB (4.41%)
angular-animate: 147.1 KB (3.29%)
angularjs-slider: 87.52 KB (1.95%)
validator: 70.85 KB (1.58%)
bootstrap-sass: 68.07 KB (1.52%)
buffer: 47.47 KB (1.06%)
angular-resource: 34.45 KB (0.769%)
angular-messages: 27.39 KB (0.612%)
angular-file-upload: 20.36 KB (0.455%)
angular-sticky-plugin: 19.06 KB (0.426%)
color-convert: 16.53 KB (0.369%)
ng-tags-input: 15.62 KB (0.349%)
angular-aria: 14.87 KB (0.332%)
angular-ui-bootstrap-datetimepicker: 13.26 KB (0.296%)
angular-chart.js: 12.7 KB (0.284%)
chartjs-color: 10.96 KB (0.245%)
chartjs-color-string: 5.9 KB (0.132%)
color-name: 4.49 KB (0.100%)
base64-js: 3.4 KB (0.0760%)
angular-validation-match: 2.06 KB (0.0460%)
ieee754: 2.01 KB (0.0448%)
css-loader: 1.47 KB (0.0328%)
webpack: 1.03 KB (0.0230%)
isarray: 132 B (0.00288%)
<self>: 622.84 KB (13.9%)

I tried checked the minified angular file is ~160kb. I guess its not using or showing the minified size. In any case, I am using Uglify so I'd expect the size to be similar?

I tried using webpack aliases, to make webpack use the minified bundles but that didnt reduce the vendor bundle much.

For the time being I split up the vendor bundles into 4 parts so they can be downloaded in parallel to speed things up but am still curious how to do better and also why is angular and other libraries so large even tho the minified code provided is significantly smaller.

My webpack config:

const webpack = require('webpack');
const conf = require('./gulp.conf');
const path = require('path');

const HtmlWebpackPlugin = require('html-webpack-plugin');
const ExtractTextPlugin = require('extract-text-webpack-plugin');
const pkg = require('../package.json');
const autoprefixer = require('autoprefixer');

const deps = Object.keys(pkg.dependencies);
const vendorNumInBundle = Math.ceil(deps.length / 4)

module.exports = {
  module: {
    preLoaders: [
      {
        test: /\.js$/,
        exclude: /node_modules/,
        loader: 'eslint'
      }
    ],

    loaders: [
      {
        test: /.json$/,
        loaders: [
          'json'
        ]
      },
      {
        test: /\.(css|less)$/,
        loaders: ExtractTextPlugin.extract(['css-loader', 'postcss-loader', 'less-loader'])
      },
      {
        test: /\.scss$/,
        loaders: ExtractTextPlugin.extract(['css-loader', 'postcss-loader', 'sass-loader'])
      },
      {
        test: /\.js$/,
        exclude: /node_modules/,
        loaders: [
          'ng-annotate'
        ]
      },
      {
        test: /.html$/,
        loaders: [
          'html'
        ]
      }
    ]
  },
  plugins: [
    new webpack.optimize.OccurrenceOrderPlugin(),
    new webpack.NoErrorsPlugin(),
    new HtmlWebpackPlugin({
      template: conf.path.src('index.html')
    }),
    new webpack.optimize.UglifyJsPlugin({
      compress: {unused: true, dead_code: true, warnings: false}, // eslint-disable-line camelcase
      comments: false
    }),
    new ExtractTextPlugin('index-[contenthash].css'),
    new webpack.optimize.CommonsChunkPlugin({
      names: ['vendor5', 'vendor4', 'vendor3', 'vendor2', 'vendor1'],
      minChunks: Infinity
    }),
    function()
    {
        this.plugin("done", function(stats)
        {
            if (stats.compilation.errors && stats.compilation.errors.length && process.argv.indexOf('--watch') == -1)
            {
                console.log(stats.compilation.errors);
                process.exit(1); // or throw new Error('webpack build failed.');
            }
            // ...
        });
    }
  ],
  postcss: () => [autoprefixer],
  output: {
    path: path.join(process.cwd(), conf.paths.dist),
    filename: '[name]-[hash].js'
  },
  entry: {
    app: `./${conf.path.src('index')}`,
    vendor1: deps.slice(0, vendorNumInBundle),
    vendor2: deps.slice(vendorNumInBundle, vendorNumInBundle * 2),
    vendor3: deps.slice(vendorNumInBundle * 2, vendorNumInBundle * 3),
    vendor4: deps.slice(vendorNumInBundle * 3),
    vendor5: [
      './src/assets/js/angular-natural-language.min.js',
      './src/assets/js/angular-img-cropper.js',
      './src/assets/js/satellizer.min.js',
      './src/assets/js/satellizer.min.js',
      './src/assets/js/angular-bootstrap-toggle',
    ]
  }
};
1
1. The win from uglify could vary from 0 to 100% depending on how the developers write the code, how the dependencies are managed, etc. So, I would not really trust the size distribution sheet you get from the webpack. 2. You should check the zipped size of the output files because a properly configured web server would serve the zipped content, so 1.6MB could easily become 0.4M or even lesssmnbbrv

1 Answers

2
votes

I guess its not using or showing the minified size.

This is correct. The output you are seeing is for the unminified versions of the libraries. See the Important Note about Minified Code in the README.

Size information for the unminified versions is useful to find out roughly the proportions of the bundles taken up by different modules. If you want to see minified sizes in the output of this tool, you'll need to configure Webpack to minify individual modules using a loader, rather than just minifying the whole bundle at the end of the process, which is what happens when you use webpack --optimize-minimize.