Webpack 構(gòu)建性能

2023-05-17 17:37 更新

本指南包含一些改進(jìn)構(gòu)建/編譯性能的實(shí)用技巧。

通用環(huán)境

無論你是在 開發(fā)環(huán)境 還是在 生產(chǎn)環(huán)境 下運(yùn)行構(gòu)建腳本,以下最佳實(shí)踐都會有所幫助。

更新到最新版本

使用最新的 webpack 版本。我們會經(jīng)常進(jìn)行性能優(yōu)化。webpack 的最新穩(wěn)定版本是:

latest webpack version

將 Node.js 更新到最新版本,也有助于提高性能。除此之外,將你的 package 管理工具(例如 npm 或者 yarn)更新到最新版本,也有助于提高性能。較新的版本能夠建立更高效的模塊樹以及提高解析速度。

loader

將 loader 應(yīng)用于最少數(shù)量的必要模塊。而非如下:

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

通過使用 include 字段,僅將 loader 應(yīng)用在實(shí)際需要將其轉(zhuǎn)換的模塊:

const path = require('path');

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

引導(dǎo)(bootstrap)

每個(gè)額外的 loader/plugin 都有其啟動時(shí)間。盡量少地使用工具。

解析

以下步驟可以提高解析速度:

  • 減少 ?resolve.modules?, ?resolve.extensions?, ?resolve.mainFiles?, ?resolve.descriptionFiles? 中條目數(shù)量,因?yàn)樗麄儠黾游募到y(tǒng)調(diào)用的次數(shù)。
  • 如果你不使用 symlinks(例如 npm link 或者 yarn link),可以設(shè)置 ?resolve.symlinks: false?。
  • 如果你使用自定義 resolve plugin 規(guī)則,并且沒有指定 context 上下文,可以設(shè)置 ?resolve.cacheWithContext: false?。

dll

使用 DllPlugin 為更改不頻繁的代碼生成單獨(dú)的編譯結(jié)果。這可以提高應(yīng)用程序的編譯速度,盡管它增加了構(gòu)建過程的復(fù)雜度。

小即是快(smaller = faster)

減少編譯結(jié)果的整體大小,以提高構(gòu)建性能。盡量保持 chunk 體積小。

  • 使用數(shù)量更少/體積更小的 library。
  • 在多頁面應(yīng)用程序中使用 SplitChunksPlugin。
  • 在多頁面應(yīng)用程序中使用 SplitChunksPlugin ,并開啟 async 模式。
  • 移除未引用代碼。
  • 只編譯你當(dāng)前正在開發(fā)的那些代碼。

worker 池(worker pool)

thread-loader 可以將非常消耗資源的 loader 分流給一個(gè) worker pool。

持久化緩存

在 webpack 配置中使用 cache 選項(xiàng)。使用 package.json 中的 "postinstall" 清除緩存目錄。

自定義 plugin/loader

對它們進(jìn)行概要分析,以免在此處引入性能問題。

Progress plugin

將 ProgressPlugin 從 webpack 中刪除,可以縮短構(gòu)建時(shí)間。請注意,ProgressPlugin 可能不會為快速構(gòu)建提供太多價(jià)值,因此,請權(quán)衡利弊再使用。

開發(fā)環(huán)境

以下步驟對于 開發(fā)環(huán)境 特別有幫助。

增量編譯

使用 webpack 的 watch mode(監(jiān)聽模式)。而不使用其他工具來 watch 文件和調(diào)用 webpack 。內(nèi)置的 watch mode 會記錄時(shí)間戳并將此信息傳遞給 compilation 以使緩存失效。

在某些配置環(huán)境中,watch mode 會回退到 poll mode(輪詢模式)。監(jiān)聽許多文件會導(dǎo)致 CPU 大量負(fù)載。在這些情況下,可以使用 watchOptions.poll 來增加輪詢的間隔時(shí)間。

在內(nèi)存中編譯

下面幾個(gè)工具通過在內(nèi)存中(而不是寫入磁盤)編譯和 serve 資源來提高性能:

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

stats.toJson 加速

webpack 4 默認(rèn)使用 stats.toJson() 輸出大量數(shù)據(jù)。除非在增量步驟中做必要的統(tǒng)計(jì),否則請避免獲取 stats 對象的部分內(nèi)容。webpack-dev-server 在 v3.1.3 以后的版本,包含一個(gè)重要的性能修復(fù),即最小化每個(gè)增量構(gòu)建步驟中,從 stats 對象獲取的數(shù)據(jù)量。

Devtool

需要注意的是不同的 devtool 設(shè)置,會導(dǎo)致性能差異。

  • "eval" 具有最好的性能,但并不能幫助你轉(zhuǎn)譯代碼。
  • 如果你能接受稍差一些的 map 質(zhì)量,可以使用 cheap-source-map 變體配置來提高性能
  • 使用 eval-source-map 變體配置進(jìn)行增量編譯。

避免在生產(chǎn)環(huán)境下才會用到的工具

某些 utility, plugin 和 loader 都只用于生產(chǎn)環(huán)境。例如,在開發(fā)環(huán)境下使用 TerserPlugin 來 minify(壓縮) 和 mangle(混淆破壞) 代碼是沒有意義的。通常在開發(fā)環(huán)境下,應(yīng)該排除以下這些工具:

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

最小化 entry chunk

Webpack 只會在文件系統(tǒng)中輸出已經(jīng)更新的 chunk。某些配置選項(xiàng)(HMR, output.chunkFilename 的 [name]/[chunkhash]/[contenthash],[fullhash])來說,除了對已經(jīng)更新的 chunk 無效之外,對于 entry chunk 也不會生效。

確保在生成 entry chunk 時(shí),盡量減少其體積以提高性能。下面的配置為運(yùn)行時(shí)代碼創(chuàng)建了一個(gè)額外的 chunk,所以它的生成代價(jià)較低:

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

避免額外的優(yōu)化步驟

Webpack 通過執(zhí)行額外的算法任務(wù),來優(yōu)化輸出結(jié)果的體積和加載性能。這些優(yōu)化適用于小型代碼庫,但是在大型代碼庫中卻非常耗費(fèi)性能:

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

輸出結(jié)果不攜帶路徑信息

Webpack 會在輸出的 bundle 中生成路徑信息。然而,在打包數(shù)千個(gè)模塊的項(xiàng)目中,這會導(dǎo)致造成垃圾回收性能壓力。在 options.output.pathinfo 設(shè)置中關(guān)閉:

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

Node.js 版本 8.9.10-9.11.1

Node.js v8.9.10 - v9.11.1 中的 ES2015 Map 和 Set 實(shí)現(xiàn),存在 性能回退。Webpack 大量地使用這些數(shù)據(jù)結(jié)構(gòu),因此這次回退也會影響編譯時(shí)間。

之前和之后的 Node.js 版本不受影響。

TypeScript loader

你可以為 loader 傳入 transpileOnly 選項(xiàng),以縮短使用 ts-loader 時(shí)的構(gòu)建時(shí)間。使用此選項(xiàng),會關(guān)閉類型檢查。如果要再次開啟類型檢查,請使用 ForkTsCheckerWebpackPlugin。使用此插件會將檢查過程移至單獨(dú)的進(jìn)程,可以加快 TypeScript 的類型檢查和 ESLint 插入的速度。

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

生產(chǎn)環(huán)境

以下步驟對于 生產(chǎn)環(huán)境 特別有幫助。

Source Maps

source map 相當(dāng)消耗資源。你真的需要它們?

工具相關(guān)問題

下列工具存在某些可能會降低構(gòu)建性能的問題:

Babel

  • 最小化項(xiàng)目中的 preset/plugin 數(shù)量。

TypeScript

  • 在單獨(dú)的進(jìn)程中使用 ?fork-ts-checker-webpack-plugin? 進(jìn)行類型檢查。
  • 配置 loader 跳過類型檢查。
  • 使用 ts-loader 時(shí),設(shè)置 ?happyPackMode: true? / ?transpileOnly: true?。

Sass

  • node-sass 中有個(gè)來自 Node.js 線程池的阻塞線程的 bug。 當(dāng)使用 thread-loader 時(shí),需要設(shè)置 workerParallelJobs: 2。


以上內(nèi)容是否對您有幫助:
在線筆記
App下載
App下載

掃描二維碼

下載編程獅App

公眾號
微信公眾號

編程獅公眾號