构建性能

本指南包含一些提高构建/编译性能的实用技巧。


通用

以下最佳实践将有所帮助,无论您是在开发还是生产中运行构建脚本。

保持最新

使用最新版本的 webpack。我们一直在进行性能改进。最新推荐的 webpack 版本是

latest webpack version

保持Node.js最新也可以提高性能。除此之外,保持您的包管理器(例如npmyarn)最新也有帮助。较新版本创建更高效的模块树并提高解析速度。

加载器

将加载器应用于最少数量的必要模块。而不是

module.exports = {
  //...
  module: {
    rules: [
      {
        test: /\.js$/,
        loader: 'babel-loader',
      },
    ],
  },
};

使用include字段仅应用实际需要转换的加载器模块

const path = require('path');

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

引导

每个额外的加载器/插件都有一个启动时间。尽量使用尽可能少的工具。

解析

以下步骤可以提高解析速度

  • 最小化resolve.modulesresolve.extensionsresolve.mainFilesresolve.descriptionFiles中的项目数量,因为它们会增加文件系统调用的数量。
  • 如果您不使用符号链接(例如npm linkyarn link),请设置resolve.symlinks: false
  • 如果您使用自定义解析插件,这些插件不是特定于上下文的,请设置resolve.cacheWithContext: false

DLL

使用DllPlugin将更改频率较低的代码移到单独的编译中。这将提高应用程序的编译速度,尽管它会增加构建过程的复杂性。

体积更小 = 速度更快

减少编译的总大小以提高构建性能。尽量保持代码块小巧。

  • 使用更少/更小的库。
  • 在多页面应用程序中使用 SplitChunksPlugin
  • 在多页面应用程序中使用 SplitChunksPluginasync 模式。
  • 删除未使用的代码。
  • 只编译当前正在开发的代码部分。

工作池

thread-loader 可用于将昂贵的加载器卸载到工作池。

持久缓存

在 webpack 配置中使用 cache 选项。在 package.json 中的 "postinstall" 中清除缓存目录。

自定义插件/加载器

对它们进行性能分析,避免引入性能问题。

进度插件

可以通过从 webpack 的配置中移除 ProgressPlugin 来缩短构建时间。请记住,ProgressPlugin 对于快速构建可能没有那么大的价值,因此请确保您正在利用使用它的优势。


开发

以下步骤在开发中特别有用。

增量构建

使用 webpack 的 watch 模式。不要使用其他工具来监视您的文件并调用 webpack。内置的 watch 模式将跟踪时间戳并将此信息传递给编译以进行缓存失效。

在某些设置中,监视会回退到轮询模式。对于许多被监视的文件,这会导致大量的 CPU 负载。在这种情况下,您可以使用 watchOptions.poll 增加轮询间隔。

内存编译

以下实用程序通过在内存中编译和提供资产来提高性能,而不是写入磁盘

  • webpack-dev-server
  • webpack-hot-middleware
  • webpack-dev-middleware

stats.toJson 速度

Webpack 4 默认情况下会使用其 stats.toJson() 输出大量数据。在增量步骤中,除非必要,否则避免检索 stats 对象的某些部分。v3.1.3 之后的 webpack-dev-server 包含一个实质性的性能修复,以最大程度地减少每次增量构建步骤从 stats 对象检索的数据量。

Devtool

注意不同 devtool 设置之间的性能差异。

  • "eval" 具有最佳性能,但不会帮助您进行转译代码。
  • 如果您能忍受稍微差一点的映射质量,cheap-source-map 变体性能更高。
  • 对于增量构建,请使用 eval-source-map 变体。

避免生产特定工具

某些实用程序、插件和加载器只有在构建生产环境时才有意义。例如,在开发过程中,使用 TerserPlugin 对代码进行压缩和混淆通常没有意义。这些工具通常应该在开发中排除。

  • TerserPlugin
  • [fullhash]/[chunkhash]/[contenthash]
  • AggressiveSplittingPlugin
  • AggressiveMergingPlugin
  • ModuleConcatenationPlugin

最小入口块

Webpack 仅将更新的块输出到文件系统。对于某些配置选项(HMR、[name]/[chunkhash]/[contenthash]output.chunkFilename 中、[fullhash]),除了更改的块之外,入口块也会失效。

确保入口块通过保持其大小来廉价地发出。以下配置为运行时代码创建了一个额外的块,因此它生成起来很便宜

module.exports = {
  // ...
  optimization: {
    runtimeChunk: true,
  },
};

避免额外的优化步骤

Webpack 会进行额外的算法工作来优化输出的大小和加载性能。这些优化对于较小的代码库来说性能很高,但在较大的代码库中可能很昂贵。

module.exports = {
  // ...
  optimization: {
    removeAvailableModules: false,
    removeEmptyChunks: false,
    splitChunks: false,
  },
};

无路径信息的输出

Webpack 能够在输出包中生成路径信息。但是,这会给捆绑数千个模块的项目带来垃圾回收压力。在 `options.output.pathinfo` 设置中关闭此功能。

module.exports = {
  // ...
  output: {
    pathinfo: false,
  },
};

Node.js 版本 8.9.10-9.11.1

在 Node.js 版本 8.9.10 - 9.11.1 中,ES2015 的 `Map` 和 `Set` 实现存在 性能回归。Webpack 广泛使用这些数据结构,因此此回归会影响编译时间。

较早和较晚的 Node.js 版本不受影响。

TypeScript 加载器

为了在使用 `ts-loader` 时提高构建速度,请使用 `transpileOnly` 加载器选项。此选项本身会关闭类型检查。要重新启用类型检查,请使用 `ForkTsCheckerWebpackPlugin`。这通过将每个操作移到单独的进程中来加速 TypeScript 类型检查和 ESLint 代码风格检查。

module.exports = {
  // ...
  test: /\.tsx?$/,
  use: [
    {
      loader: 'ts-loader',
      options: {
        transpileOnly: true,
      },
    },
  ],
};

生产环境

以下步骤在生产环境中特别有用。

源映射

源映射非常昂贵。你真的需要它们吗?


特定工具问题

以下工具存在某些问题,可能会降低构建性能

Babel

  • 最小化预设/插件的数量

TypeScript

  • 使用 `fork-ts-checker-webpack-plugin` 在单独的进程中进行类型检查。
  • 配置加载器以跳过类型检查。
  • happyPackMode: true / transpileOnly: true 中使用 ts-loader

Sass

  • node-sass 存在一个 bug,它会阻止 Node.js 线程池中的线程。当与 thread-loader 一起使用时,请将 workerParallelJobs 设置为 2

6 位贡献者

sokratbroadleybyzykmadhavarshneywizardofhogwartsanikethsaha