banner
他山之石

他山之石

Webpack build speed optimization

This article is a summary of my experience using Webpack 4, introducing some methods to optimize the speed of Webpack build.

image

Webpack is currently the most popular bundling tool, and the latest version is Webpack 4+, which has greatly improved performance. This article is a summary of my experience using Webpack, mainly focusing on improving the build performance of Webpack.

Keep the Version Updated#

Use the latest version of webpack. New versions generally include performance optimizations.

Keeping the Node.js version up to date can also improve performance. In addition, ensuring that your package manager (such as npm or yarn) is up to date can also improve performance. Newer versions can build more efficient module dependency trees and improve parsing speed.

Use include or exclude in Loaders#

When configuring loaders, using include can more accurately specify the directories to be processed, reducing unnecessary traversal and thus reducing performance loss. Similarly, directories that are already known and do not need to be processed should be excluded to further improve performance. Therefore, setting include and exclude reasonably will greatly improve the optimization speed of Webpack packaging.

module.exports = {
  //...
  module: {
    rules: [
      {
        test: /\.js$/,
        include: path.resolve(__dirname, 'src'),
        loader: 'babel-loader'
      }
    ]
  }
};

Improve Parsing Speed#

The following steps can improve parsing speed:

  • Try to reduce the number of categories in resolve.modules, resolve.extensions, resolve.mainFiles, and resolve.descriptionFiles, as they will increase the number of file system calls.
  • If you do not use symlinks, you can set resolve.symlinks: false (e.g. npm link or yarn link).
  • If you use custom resolution plugins and have not specified context information, you can set resolve.cacheWithContext: false.

module.noParse#

Prevent webpack from parsing files that match a given regular expression. The ignored files should not contain calls to import, require, define, or any other import mechanism. Ignoring large libraries can improve build performance.

module.exports = {
  //...
  module: {
    noParse: /jquery|lodash/,

    // Starting from webpack 3.0.0
    noParse: function(content) {
      return /jquery|lodash/.test(content);
    }
  }
};

babel-loader#

babel-loader is slow to build. It not only needs to use exclude and include to specify the directory to be compiled as accurately as possible, but also needs to make full use of caching to further improve performance. babel-loader provides a specific option called cacheDirectory (default false). When set, the specified directory will be used to cache the execution results of the loader. Subsequent webpack builds will try to read the cache to avoid the potentially high-performance-consuming Babel recompilation process that may occur each time it is executed.

By using the cacheDirectory option, babel-loader can be accelerated at least twice. This will cache the transpiled results to the file system.

Babel inserts helper code into each file, making the code size too large!
Babel uses very small helper code for some common methods, such as _extend. By default, it will be added to every file that needs it.

You can import babel runtime as a standalone module to avoid duplicate imports.

The following configuration disables babel's automatic injection of runtime into each file and instead introduces babel-plugin-transform-runtime and references all helper code from here.

rules: [
  // The 'transform-runtime' plugin tells babel to use the runtime instead of injecting it.
  {
    test: /\.js$/,
    exclude: /(node_modules|bower_components)/,
    use: {
      loader: 'babel-loader',
      options: {
        presets: ['@babel/preset-env'],
        plugins: ['@babel/transform-runtime']
      }
    }
  }
]

Dlls#

Use DllPlugin to compile infrequently changed code separately. This will improve the compilation speed of the application, even though it adds complexity to the build process.

Happypack Multi-threaded Build#

Due to the single-process limitation of Node.js, although all loaders are called concurrently in async form, they still run in a single node process and in the same event loop. This directly leads to some problems: when reading multiple loader file resources at the same time, such as babel-loader needing to transform various jsx and es6 resource files. In this CPU-intensive scenario, the single-process model of Node has no advantage, and Happypack is born to solve such problems.

The idea of Happypack is to extend the original webpack's execution process of loaders from a single-process form to a multi-process mode, thereby accelerating code compilation; the original process remains unchanged, so that optimization of the compilation process can be achieved without modifying the original configuration. The specific configuration is as follows:

// webpack.config.js
const HappyPack = require('happypack');
const happyThreadPool = HappyPack.ThreadPool({ size: os.cpus().length });

exports.module = {
  rules: [
    {
      test: /.js$/,
      include: [resolve('src')],
      exclude: /node_modules/,
      loader: 'happypack/loader?id=happybabel'
    }
  ]
};

exports.plugins = [
  new HappyPack({
    id: 'happybabel',
    loaders: ['babel-loader'],
    threadPool: happyThreadPool,
    cache: true,
    verbose: true
  })
];

During the compilation process, Happypack not only uses multi-threaded mode to speed up compilation, but also enables cache calculation, which can fully utilize cache to read build files. The speed improvement of the build is also very obvious.

Loading...
Ownership of this post data is guaranteed by blockchain and smart contracts to the creator alone.