Saturday, 8 April 2017

How do I split my webpack bundles automatically, whilst staying under the recommended 250kB per bundle?

I've followed pretty much all the Webpack 2 tutorials there are on code-splitting, but none of them seem to address the problem of code-splitting to a maximum bundle size. Since Webpack 2, the build emits a warning when a bundle is over 250kB. So what I'm trying to do is split my build into:

  • Application code (so everything I've written myself)
  • Async code
  • Vendor code (everything from node_modules)
  • A manifest (so that the bundle hash doesn't change unnecessarily across builds)

Now the vendor code bundle goes over 250kB, so I want to split that into bundles of 250kB max. But there doesn't seem to be any documentation on how to do that. I'm currently using the following webpack config for production builds:

var PreloadWebpackPlugin = require('preload-webpack-plugin')
var CleanWebpackPlugin = require('clean-webpack-plugin')
var ExtractTextPlugin = require('extract-text-webpack-plugin')
var HtmlWebpackPlugin = require('html-webpack-plugin')
var CopyWebpackPlugin = require('copy-webpack-plugin')
var path = require('path')
var webpack = require('webpack')

// Define two different css extractors
var extractBundle = new ExtractTextPlugin('[name]-[hash].css')
var extractVendor = new ExtractTextPlugin('vendor-[hash].css')

module.exports = {
  entry: {
    main: './client/index.jsx'
  },
  output: {
    filename: '[name].[chunkhash].js',
    path: path.join(__dirname, 'dist')
  },
  resolve: {
    extensions: ['.js', '.jsx'],
    modules: [
      './client',
      'node_modules'
    ]
  },
  module: {
    rules: [
      {
        test: /\.js$|\.jsx$/,
        exclude: /(node_modules)/,
        loader: 'babel-loader'
      },
      {
        test: /\.css$/,
        include: /node_modules/,
        loader: extractVendor.extract({
          fallback: 'style-loader?sourceMap',
          use: 'css-loader?sourceMap'
        })
      },
      {
        test: /\.css$/,
        exclude: /node_modules/,
        loader: extractBundle.extract({
          fallback: 'style-loader?sourceMap',
          use: 'css-loader?sourceMap'
        })
      }
    ]
  },
  devtool: 'source-map',
  plugins: [
    extractVendor,
    extractBundle,
    new CleanWebpackPlugin(
      path.join(__dirname, 'dist'),
      { verbose: false }
    ),
    new CopyWebpackPlugin([{ from: path.join(__dirname, 'static')}]),
    new webpack.optimize.UglifyJsPlugin({
      sourceMap: true
    }),
    new webpack.EnvironmentPlugin([
      'NODE_ENV',
      'SPACE_ID',
      'CONTENT_DELIVERY_TOKEN',
      'PROD_TRACKING_ID'
    ]),
    new HtmlWebpackPlugin({
      template: path.join(__dirname, 'client', 'index.ejs'),
      filename: 'index.html',
      minify: {
        collapseWhitespace: true,
        removeComments: true
      }
    }),
    new PreloadWebpackPlugin({
      rel: 'preload'
    }),
    new webpack.optimize.CommonsChunkPlugin({
      name: "vendor",
      minChunks: function(module){
        return module.context && module.context.indexOf("node_modules") !== -1;
      }
    }),
    new webpack.optimize.CommonsChunkPlugin({
      name: "manifest",
      minChunks: Infinity
    })
  ]
}

Which outputs:

                               Asset       Size  Chunks                    Chunk Names
          android-chrome-192x192.png    20.1 kB          [emitted]
           0.a473279b795f515798d3.js    4.52 kB       0  [emitted]
        main.4cabac8591610d12205d.js    33.2 kB       2  [emitted]         main
    manifest.b0393c03cc63335ff287.js    1.51 kB       3  [emitted]         manifest
     vendor-4a38a077b20fa163ffd6.css    2.21 kB       1  [emitted]         vendor
       main-4a38a077b20fa163ffd6.css  492 bytes       2  [emitted]         main
       0.a473279b795f515798d3.js.map    35.3 kB       0  [emitted]
  vendor.04e2ff4d4cdd8e49d6ad.js.map    3.83 MB       1  [emitted]         vendor
 vendor-4a38a077b20fa163ffd6.css.map     8.9 kB       1  [emitted]         vendor
    main.4cabac8591610d12205d.js.map     209 kB       2  [emitted]         main
   main-4a38a077b20fa163ffd6.css.map    1.32 kB       2  [emitted]         main
manifest.b0393c03cc63335ff287.js.map    14.3 kB       3  [emitted]         manifest
      vendor.04e2ff4d4cdd8e49d6ad.js     436 kB       1  [emitted]  [big]  vendor
                apple-touch-icon.png    16.1 kB          [emitted]
                   browserconfig.xml  246 bytes          [emitted]
                   favicon-16x16.png  846 bytes          [emitted]
                   favicon-32x32.png    1.93 kB          [emitted]
          android-chrome-512x512.png     128 kB          [emitted]
                         favicon.ico    15.1 kB          [emitted]
                       manifest.json  385 bytes          [emitted]
                  mstile-150x150.png     1.5 kB          [emitted]
                          robots.txt   24 bytes          [emitted]
               safari-pinned-tab.svg    35.3 kB          [emitted]
                          index.html    1.35 kB          [emitted]

How can I automatically split the vendor bundle into chunks that don't exceed the recommended 250kB?



via ismay

No comments:

Post a Comment