淺談 webpack 性能優化(內附巨詳細 webpack 學習筆記)

前言

筆者最近在整理關於 webpack 相關的知識點,一方面是由於本身掌握的知識點比較零碎、不夠系統,有時候碰到問題不知從何下手,另一方面 webpack5.0 已經在路上了,這的確是一個讓人頭禿的消息。javascript

因此這就促使了我去系統性的回顧了一遍 webpack4.0 的全部知識點,包括 webpack 的由來,各類配置的使用、性能優化、Webpack 的底層原理、相關腳手架的配置分析,都回顧了一波,大體目錄以下圖:css

筆者把系列的文章都扔在了這個倉庫:webpack 學習整理文檔,有興趣的同窗能夠去看一波。html

今天這篇文章也是筆者就學習文檔中的性能優化這一塊內容作的整理與回顧。前端

文章中使用到的案例代碼連接放在了最底部,你們自取。

 

爲何要優化

先來講說爲何要優化?固然若是你的項目很小,構建很快,其實不須要特別關注性能方面的問題。java

可是隨着項目涉及到的頁面愈來愈多,功能和業務代碼也會愈來愈多,相應的 webpack 的構建時間也會愈來愈久,這個時候咱們就不得不考慮性能優化的事情了。node

由於這個構建時間與咱們的平常開發是密切相關,當咱們本地開發啓動 devServer 或者 build 的時候,若是時間過長,會大大下降咱們的工做效率。react

試想一個場景,咱們忽然碰到一個緊急 bug,項目啓動須要花費 3/4 分鐘,改完後項目 build 上線也要 3/4 分鐘,這個時候腦瓜是否是 duangduangduang...webpack

那接下來咱們看一下如何優化 webpack 的性能,提高 webpack 的構建速度。git

 

分析工具

在動手優化以前,咱們須要有一個量化的指標,得知道影響構建時間的問題究竟出在哪裏,是某個 chunk 文件太大了,仍是哪個 loader 或者 plugin 耗時過久了等等。github

咱們能夠對經過一些工具對項目進行相應的 體積速度 分析, 而後對症下藥。

體積分析

初級分析

能夠經過官方提供的 stat.json 文件幫助咱們分析打包結果,stat.json 文件能夠經過下面語句快速生成:

webpack --profile --json > stats.json

接着咱們經過官網提供的 stats.json 分析工具 進行分析,上傳 stats.json 文件以後,就能夠獲得以下圖所示分析結果:

其中包括 webpack 的版本、打包時間、打包過程的 hash 值、模塊數量( modules )、chunk 數量、打包生層的靜態文件 assets 以及打包的警告和錯誤數。

咱們能夠分析其提供的內容,進行大體問題的定位。

第三方工具

webpack-bundle-analyzer 是打包分析神器,它的界面我的以爲很好看,並且能很直觀的給出每個打包出來的文件的大小以及各自的依賴,可以更加方便的幫助咱們對項目進行分析。

使用以下:

// config/webpack.common.js
const { BundleAnalyzerPlugin } = require('webpack-bundle-analyzer');

const commonConfig = {
  // ...
  plugins: [
    new BundleAnalyzerPlugin({
      analyzerPort: 8889, // 指定端口號
      openAnalyzer: false,
    }),
  ]
  // ...
}
webpack-bundle-analyzer 其底層也是依賴 stat.json 文件的,經過對 stat.json 的分析,得出最後的分析頁面

經過分析工具的分析,咱們能夠知道哪些文件耗時比較多,打包出來的體積比較大,從而對有問題的文件進行優化。

 

速度分析

咱們能夠經過 speed-measure-webpack-plugin 這個插件幫助咱們分析整個打包的總耗時,以及每個loader 和每個 plugins 構建所耗費的時間,從而幫助咱們快速定位到能夠優化 Webpack 的配置。

如上圖,耗時比較長的會以紅色標出。

使用

引入此插件,建立一個 plugins 實例 smp 包裹 webpack 配置文件便可,咱們修改一下 webpack 的公共配置文件 webpack.common.js

// config/webpack.common.js
const SpeedMeasurePlugin = require("speed-measure-webpack-plugin");
const smp = new SpeedMeasurePlugin();
// ...
module.exports = (production) => {
  if (production) {
    const endProdConfig = merge(commonConfig, prodConfig);
    return smp.wrap(endProdConfig);
  } else {
    const endDevConfig = merge(commonConfig, devConfig);
    return smp.wrap(endDevConfig);
  }
};

筆者文章演示的代碼配置文件分爲三個,分別爲 開發環境配置文件生產環境配置文件,以及前兩者共用的公共配置文件,以下:

  • webpack.dev.js:開發環境使用的配置文件
  • webpack.prod.js:生產環境使用的配置文件
  • webpack.common.js:公共配置文件

執行打包以後,能夠看到以下效果圖:

注意: speed-measure-webpack-plugin 對於 webpack 的升級還不夠完善,暫時還沒法與你本身編寫的掛載在 html-webpack-plugin 提供的 hooks 上的自定義 Pluginadd-asset-html-webpack-plugin 就是此類)共存,有人已經在 github 上提了 issue 了,可是貌似仍是沒有解決。

 

優化策略

通過相應的體積分析和速度分析以後,咱們即可以着手進行優化了。

使用新版本

這個是 webpack 性能優化的萬能膏藥,升級版本一定能帶來性能提高,並且提高很明顯。

咱們能夠看一張對比圖:

從上圖中咱們能夠看到, webpack4.0 的構建速度遠遠快於 webpack3.0,官方也說升級以後,升級版本以後,構建時間能夠下降 60% - 98% 左右。

在每個版本的更新,webpack 內部確定會作不少優化,而 webpack 是依賴 Nodejs 運行環境,升級他們對應的版本,webpack 的速度確定也可以得到提高。

說不定在 webpack5.0 出來以後,咱們今天講到的大部分性能優化方法都會被集成到 webpack 自身中去,咱們只須要經過幾個簡單的配置就能完成性能配置。

同時新版本的包管理工具(NpmYarn)也能夠更快的幫助咱們分析一些包的依賴和引入,從而提升打包速度。

webpack4.0 帶來的優化

  • v8 引擎帶來的優化(for of 替代 forEachMapSet 替代 Objectincludes 替代 indexOf
  • 默認使用更快的 md4 hash 算法
  • webpack AST 能夠直接從 loader 傳遞給 AST,減小解析時間
  • 使用字符串方法替代正則表達式

咱們能夠在 github 上的 webpack 庫的 releases 版本迭代 頁面中查看其帶來的性能優化:

一個 v8 性能優化例子:

咱們能夠來看一個例子,比較使用 includes 替代 indexOf 以後帶來的速度提高,建立 compare-includes-indexof.js 文件,在這個文件中建一個 10000000 長度的數組,記錄兩個函數分別消耗的時間:

const ARR_SIZE = 10000000;
const hugeArr = new Array(ARR_SIZE).fill(1);

// includes
const includesTest = () => {
  const arrCopy = [];
  console.time('includes')
  let i = 0;
  while (i < hugeArr.length) {
    arrCopy.includes(i++);
  }
  console.timeEnd('includes');
}

// indexOf
const indexOfTest = () => {
  const arrCopy = [];
  console.time('indexOf');
  for (let item of hugeArr) {
    arrCopy.indexOf(item);
  }
  console.timeEnd('indexOf');
}

includesTest();
indexOfTest();

能夠發現 includes 的速度遠遠快於 indexOf

  • includes12.224ms
  • indexOf147.638ms

因此在項目上儘量使用比較新的 webpackNodeNpmYarn 版本,是咱們提高打包速度的第一步。

 

體積優化

webpack 是個項目打包工具,通常項目打完包之後,須要發佈到服務器上供用戶使用,爲了用戶體驗,咱們的項目體積須要越小越好,因此 webpack 中打包的體積是 webpack 中重要的一環。

js 壓縮

webpack4.0 默認在生產環境的時候是支持代碼壓縮的,即 mode=production 模式下。

實際上 webpack4.0 默認是使用 terser-webpack-plugin 這個壓縮插件,在此以前是使用 uglifyjs-webpack-plugin,二者的區別是後者對 ES6 的壓縮不是很好,同時咱們能夠開啓 parallel 參數,使用多進程壓縮,加快壓縮。

// config/webpack.common.js
const TerserPlugin = require('terser-webpack-plugin');
// ...
const commonConfig = {
  // ...
  optimization: {
    minimize: true,
    minimizer: [
      new TerserPlugin({
        parallel: 4, // 開啓幾個進程來處理壓縮,默認是 os.cpus().length - 1
      }),
    ],
  },
  // ...
}

CSS 壓縮

壓縮 CSS

咱們能夠藉助 optimize-css-assets-webpack-plugin 插件來壓縮 css,其默認使用的壓縮引擎是 cssnano。 具體使用以下:

// config/webpack.prod.js
const OptimizeCSSAssetsPlugin = require("optimize-css-assets-webpack-plugin");
// ...
const prodConfig = {
  // ...
  optimization: {
    minimizer: [
      new OptimizeCSSAssetsPlugin({
        assetNameRegExp: /\.optimize\.css$/g,
        cssProcessor: require('cssnano'),
        cssProcessorPluginOptions: {
          preset: ['default', { discardComments: { removeAll: true } }],
        },
        canPrint: true,
      })
    ]
  },
}
擦除無用的 CSS

使用 PurgeCSS 來完成對無用 css 的擦除,它須要和 mini-css-extract-plugin 配合使用。

// config/webpack.common.js
const PurgecssPlugin = require('purgecss-webpack-plugin');
// ...
const PATHS = {
  src: path.join(__dirname, './src')
};

const commonConfig = {
  // ...
  plugins: [
    // ...
    new PurgecssPlugin({
      paths: glob.sync(`${PATHS.src}/**/*`,  { nodir: true }),
    }),
  ]
  // ...
}

在未使用此插件以前,好比咱們只用到了 navcontact 這個類,其餘的都沒有用到,咱們在未引入以前打包一下,發現未用到的 css 仍是會被打包進去:

引入插件後,從新進行打包,發現沒有用到的 css 都被擦除了:

更多使用你們可參考 PurgeCSS 文檔

圖片壓縮

通常來講在打包以後,一些圖片文件的大小是遠遠要比 js 或者 css 文件要來的大,因此咱們首先要作的就是對於圖片的優化,咱們能夠手動的去經過線上的圖片壓縮工具,如 tiny png 幫咱們來壓縮圖片。

可是這個比較繁瑣,在項目中咱們但願可以更加自動化一點,自動幫咱們作好圖片壓縮,這個時候咱們就能夠藉助 image-webpack-loader 幫助咱們來實現。它是基於 imagemin 這個 Node 庫來實現圖片壓縮的。

使用很簡單,咱們只要在 file-loader 以後加入 image-webpack-loader 便可:

// config/webpack.common.js
// ...
module: {
  rules: [
    {
      test: /\.(png|jpg|gif)$/,
      use: [
        {
          loader: 'file-loader',
          options: {
            name: '[name]_[hash].[ext]',
            outputPath: 'images/',
          }
        },
        {
          loader: 'image-webpack-loader',
          options: {
            // 壓縮 jpeg 的配置
            mozjpeg: {
              progressive: true,
              quality: 65
            },
            // 使用 imagemin**-optipng 壓縮 png,enable: false 爲關閉
            optipng: {
              enabled: false,
            },
            // 使用 imagemin-pngquant 壓縮 png
            pngquant: {
              quality: '65-90',
              speed: 4
            },
            // 壓縮 gif 的配置
            gifsicle: {
              interlaced: false,
            },
            // 開啓 webp,會把 jpg 和 png 圖片壓縮爲 webp 格式
            webp: {
              quality: 75
            }
          }
        }
      ]
    },
  ]
}         
// ...

咱們先不使用這個 loader 打包一下,圖片大小是 2.1MB

使用 image-webpack-loader 以後,圖片大小是 666KB

壓縮的效果仍是很明顯的。

拆分代碼

有時候咱們寫的某些模塊根本沒有使用,可是仍是被打包了,這樣實際上會拖累 webpack 的打包速度,並且也會增長打包文件的體積,因此咱們可使用 tree-shaking 將這些代碼剔除掉。

或者也可使用 splitChunksPlugin 把一個大的文件分割成幾個小的文件,這樣也能夠有效的提高 webpack 的打包速度,詳細的配置介紹你們能夠看筆者寫的 配置 SplitChunksPlugin,裏面詳細介紹了怎麼配置 splitChunks,以及各參數的用法與意義,在這裏就不展開講了。

 

速度優化

講完打包體積的優化,咱們來看一下在速度方面的優化。

分離兩套配置

通常來講在項目開發中,咱們會區分開發和生產環境兩套配置,各司其職。

在開發階段:咱們須要 webpack-dev-server 來幫咱們進行快速的開發,同時須要 HMR 熱更新 幫咱們進行頁面的無刷新改動,而這些在 生產環境 中都是不須要的。

在生產階段:咱們須要進行 代碼壓縮目錄清理計算 hash提取 CSS 等等;

實現起來很簡單,咱們前面也提到過,就新建三個 webpack 的配置文件就行:

  • webpack.dev.js:開發環境的配置文件
  • webpack.prod.js:生產環境的配置文件
  • webpack.common.js:公共配置文件

經過 webpack-merge 來整合兩個配置文件共同的配置 webpack.common.js,具體能夠參照源碼。

 

減小查找過程

webpackresolve 參數進行合理配置,使用 resolve 字段告訴 webpack 怎麼去搜索文件。

合理使用 resolve.extensions

在導入語句沒帶文件後綴時,webpack 會自動帶上後綴後去嘗試詢問文件是否存在,查詢的順序是按照咱們配置 的 resolve.extensions 順序從前到後查找,webpack 默認支持的後綴是 jsjson

舉個🌰:若是咱們配置 resolve.extensions= ['js', 'json'],那麼 webpack 會先找 xxx.js

若是沒有則再查找 xxx.json,因此咱們應該把經常使用到的文件後綴寫在前面,或者 咱們導入模塊時,儘可能帶上文件後綴名。

雖然 extensions 會優先查找數組內的值,可是咱們不要一古腦兒的把全部後綴都往裏面塞,這會調用屢次文件的查找,這樣就會減慢打包速度。
優化 resolve.modules

這個屬性告訴 webpack 解析模塊時應該搜索的目錄,絕對路徑和相對路徑都能使用。使用絕對路徑以後,將只在給定目錄中搜索,從而減小模塊的搜索層級:

// config/webpack.common.js
// ...

const commonConfig = {
  // ...
  resolve: {
    extensions: ['.js', '.jsx'],
    mainFiles: ['index', 'list'],
    alias: {
      alias: path.resolve(__dirname, '../src/alias'),
    },
    modules: [
      path.resolve(__dirname, 'node_modules'), // 指定當前目錄下的 node_modules 優先查找
      'node_modules', // 將默認寫法放在後面
    ]
  },
  // ...
}
// ...
使用 resolve.alias 減小查找過程

alias 的意思爲 別名,能把原導入路徑映射成一個新的導入路徑。

好比咱們項目中可能會有一些相對路徑的寫法,就可使用 alias 配置來減小查找過程;

還好比咱們常用的 react 庫,其實咱們能夠直接使用其 dist 目錄下打包好的 react.min.js,這樣就能跳過耗時的模塊解析,具體示例配置以下:

// config/webpack.common.js
// ...
const commonConfig = {
  // ...
  resolve: {
    // ...
    alias: {
      react: path.resolve(__dirname, './node_modules/react/dist/react.min.js'),
      @alias: path.resolve(__dirname, '../src/alias'),
    },
  },
  // ...
}
// ...
這個筆者在實際項目中沒有嘗試過,不過也是一個思路,你們有機會能夠嘗試一波。

 

縮小構建目標

排除 Webpack 不須要解析的模塊,即便用 loader 的時候,在儘可能少的模塊中去使用。

咱們能夠藉助 includeexclude 這兩個參數,規定 loader 只在那些模塊應用和在哪些模塊不該用。

咱們修改公共配置文件 webpack.common.js

// config/webpack.common.js
// ...
const commonConfig = {
  // ...
  module: {
    rules: [
      { 
        test: /\.js|jsx$/, 
        exclude: /node_modules/,
        include: path.resolve(__dirname, '../src'),
        use: ['babel-loader']
      },
      // ...
    ]
  },
}
// ...

首先咱們不加 excludeinclude 兩個參數,打包一下 npm run build,打包時間 3350ms 左右:

接着咱們加上這兩個參數,意思分別是:

  • exclude: /node_modules/:排除 node_modules 下面的文件
  • include: path.resolve(__dirname, '../src'):只對 src 下面的文件使用

從新打包一下,打包時間變成了 1400ms 左右:

 

利用多線程提高構建速度

因爲運行在 Node.js 之上的 webpack 是單線程模型的,因此 webpack 須要處理的事情須要一件一件的作,不能多件事一塊兒作。

若是 webpack 能同一時間處理多個任務,發揮多核 CPU 電腦的威力,那麼對其打包速度的提高確定是有很大的做用的。

HappyPack

原理:每次 webapck 解析一個模塊,HappyPack 會將它及它的依賴分配給 worker 線程中。處理完成以後,再將處理好的資源返回給 HappyPack 的主進程,從而加快打包速度。

webpack4.0 中使用 happypack 須要使用其 5.0 版本。

咱們將 HappyPack 引入公共配置文件,他的用法就是將相應的 loader 替換成 happypack/loader,同時將替換的 loader 放入其插件的 loaders 選項,咱們暫且替換一下 babel-loader

// config/webpack.common.js
// ...
const makePlugins = (configs) => {
  const plugins = [
    // ...
    new HappyPack({
      loaders: ['babel-loader']
    }),
  ];
  // ...
  return plugins;
}
// ...

const commonConfig = {
  entry: {
    main: "./src/index.js",
    entry2: "./src/entry2.js",
    entry3: "./src/entry3.js",
    entry4: "./src/entry4.js",
    entry5: "./src/entry5.js",
    entry6: "./src/entry6.js",
  },
  // ...
  module: {
    rules: [{ 
      test: /\.jsx?$/, 
      // exclude: /node_modules/,
      // include: path.resolve(__dirname, '../src'), 
      use: [
        'happypack/loader'
        // 'babel-loader'
      ]
    }]
  },
  // ...
}
// ...

爲了讓效果更加明顯一點,咱們在項目下多增長几個入口文件,在不使用 happypack 的狀況下,進行一次打包,時間差很少是 8s 多:

開啓 happypack 以後,咱們能夠從控制檯中看到,happypack 默認幫咱們開啓了 3 個進程,打包時間變成了6.5s 左右:

注意: HappyPack 的做者如今基本上也不維護這個插件了,由於做者對此項目的興趣正在減弱。他也推薦咱們使用 webpack 官方 thread-loader

更多參數你們能夠參考 HappyPack 官網

thread-loader

webpack 官方推出的一個多進程方案,用來替代 HappyPack

原理和 HappyPack 相似,webpack 每次解析一個模塊,thread-loader 會將它及它的依賴分配給 worker 線程中,從而達到多進程打包的目的。

使用很簡單,直接在咱們使用的 loader 以前加上 thread-loader 就行,咱們須要先註釋掉 HappyPack 代碼:

// config/webpack.common.js
// ...
const commonConfig = {
  // ...
  module: {
    rules: [{ 
      test: /\.jsx?$/, 
      // exclude: /node_modules/,
      // include: path.resolve(__dirname, '../src'), 
      use: [
        {
          loader: 'thread-loader',
          options: {
            workers: 3, // 開啓幾個 worker 進程來處理打包,默認是 os.cpus().length - 1
          }
        },
        'babel-loader'
      ]
    }]
  },
  // ...
}
// ...

咱們從新運行一下,也是差很少 6.5s 左右:

預先編譯資源模塊(DllPlugin)

咱們在打包的時候,通常來講第三方模塊是不會變化的,因此咱們想只要在第一次打包的時候去打包一下第三方模塊,並將第三方模塊打包到一個特定的文件中,當第二次 webpack 進行打包的時候,就不須要去 node_modules 中去引入第三方模塊,而是直接使用咱們第一次打包的第三方模塊的文件就行。

webpack.DllPlugin 就是來解決這個問題的插件,使用它能夠在第一次編譯打包後就生成一份不變的代碼供其餘模塊引用,這樣下一次構建的時候就能夠節省開發時編譯打包的時間。

添加配置文件

咱們在配置文件目錄 config 下新建一個 webpack.dll.js,此文件用於將咱們的第三方包文件打包到 dll 文件夾中去:

// config/webpack.dll.js
const path = require('path');
const webpack = require('webpack');

module.exports = {
  mode: 'production', // 環境
  entry: {
    vendors: ['lodash'], // 將 lodash 打包到 vendors.js 下
    react: ['react', 'react-dom'], // 將 react 和 react-dom 打包到 react.js 下
  },
  output: {
    filename: '[name].dll.js', // 輸出的名字
    path: path.resolve(__dirname, '../dll'), // 輸出的文件目錄
    library: '[name]' // 將咱們打包出來的文件以所有變量的形式暴露,能夠在瀏覽器變量的名字進行訪問
  },
  plugins: [
    // 對生成的庫文件進行分析,生成庫文件與業務文件的映射關係,將結果放在 mainfest.json 文件中
    new webpack.DllPlugin({
      name: '[name]', // 和上面的 library 輸出的名字要相同
      path: path.resolve(__dirname, '../dll/[name].manifest.json'),
    })
  ]
}
  • 上面的 library 的意思其實就是將 dll 文件以一個全局變量的形式導出出去,便於接下來引用,以下圖:
  • mainfest.json 文件是一個映射關係,它的做用就是幫助 webpack 使用咱們以前打包好的 ***.dll.js 文件,而不是從新再去 node_modules 中去尋找。

咱們在命令行中打包一下 dll 文件,能夠看到根目錄生成了一個 dll 文件夾,而且在下面生成了相應的文件,而且 loader 打包到了 vendor.dll.js 中,reactreact-dom 打包到了 react.dll.js 中了:

接着咱們須要去修改公共配置文件 webpack.common.js,將咱們以前生成的 dll 文件導入到 html 中去,若是咱們不想本身手動向 html 文件去添加 dll 文件的時候,咱們能夠藉助一個插件 add-asset-html-webpack-plugin,此插件顧名思義,就是將一些文件加到 html 中去。

同時咱們須要使用 webpack 自帶的 DllReferencePlugin 插件對 mainfest.json 映射文件進行分析。

// config/webpack.common.js
const webpack = require('webpack');
const AddAssetHtmlWebpackPlugin = require('add-asset-html-webpack-plugin');

// ...

const commonConfig = {
  // ...
  plugins: [
    // ...
    new AddAssetHtmlWebpackPlugin({
      filepath: path.resolve(__dirname, '../dll/vendors.dll.js')
    }),
    new AddAssetHtmlWebpackPlugin({
      filepath: path.resolve(__dirname, '../dll/react.dll.js')
    }),
    new webpack.DllReferencePlugin({
      manifest: require(path.resolve(__dirname, '../dll/vendors.dll.mainfest.json'))
    }),
    new webpack.DllReferencePlugin({
      manifest: require(path.resolve(__dirname, '../dll/react.dll.mainfest.json'))
    }),
  ],
  // ...
}
// ...
這裏的代碼還能夠優化,具體你們能夠參考筆者整理的筆記中 dll優化 這一節。

咱們進行一次打包,能夠看到打包耗時爲 1450ms 左右,同時能夠看到庫文件打包到的 vendors.chunk.js1.22MB

咱們註釋掉對 dll 的引用分析以後,從新打包,打包耗時爲 1950ms 左右,同時能夠看到 vendors.chunk.js5.28MB

緩存 Cache 相關

咱們能夠開啓相應 loader 或者 plugin 的緩存,來提高二次構建的速度。通常咱們能夠經過下面幾項來完成:

若是項目中有緩存的話,在 node_modules 下會有相應的 .cache 目錄來存放相應的緩存。

babel-loader

首先咱們開啓 babel-loader 的緩存,咱們修改 babel-loader 的參數,將參數 cacheDirectory 設置爲 true

// config/webpack.common.js
// ...
module: {
  rules: [
    { 
      test: /\.jsx?$/, 
      // exclude: /node_modules/,
      // include: path.resolve(__dirname, '../src'), 
      use: [
        {
          loader: 'babel-loader',
          options: {
            cacheDirectory: true,
          }
        },
      ]
    },
  ]
}         
// ...

首次打包時間爲 8.5s 左右,打包完成以後,咱們能夠發如今 node_modules 下生成了一個 .cache 目錄,裏面存放了 babel 的緩存文件:

咱們從新打包一次,會發現時間變成了 6s 左右:

TerserPlugin

咱們經過將 TerserPlugin 中的 cache 設爲 true,就能夠開啓緩存:

// config/webpack.common.js
const TerserPlugin = require('terser-webpack-plugin');
// ...
const commonConfig = {
  // ...
  optimization: {
    minimize: true,
    minimizer: [
      new TerserPlugin({
        parallel: 4, // 開啓幾個進程來處理壓縮,默認是 os.cpus().length - 1
        cache: true,
      }),
    ],
  },
  // ...
}

首次打包時間爲 8-9s 左右,同時在 .cache 目錄下生成了 terser-webpack-plugin 緩存目錄:

咱們從新打包一次,會發現時間變成了 5s 左右:

HardSourceWebpackPlugin

這個插件其實就是用於給模塊提供一箇中間的緩存。

使用以下,咱們直接在插件中引入就 ok 了:

// config/webpack.common.js
const HardSourceWebpackPlugin = require('hard-source-webpack-plugin');
// ...
const plugins = [
  // ...
  new HardSourceWebpackPlugin(),
];
// ...

咱們打包一下,能夠看到在第一次打包的時候 HardSourceWebpackPlugin 就幫咱們開始生成打包文件了,同時在 .cache 目錄生成了 hard-source 目錄,第一次打包耗時 6.6s 左右:

咱們從新打包一次,會發現時間變成了 2.7s 左右:

合理使用 sourceMap

以前咱們有講過,以前咱們打包生成 sourceMap 的時候,若是信息越詳細,打包速度就會越慢

因此咱們要在代碼打包過程當中的時候,在對應的環境使用對應的 sourceMap 很重要。

 

其餘

除了上述咱們提到的一些經常使用方法,還有其餘的一些方法,好比:

  • 使用 ES6 Modules 語法,以保證 Tree-Shaking 起做用
由於 tree-shaking 只對 ES6 Modules 靜態引入生效,對於相似於 CommonJs 的動態引入方式是無效的
  • 合理使用 Ployfill
若是咱們對於引入的 polyfill 不作處理的話, Webpack 會把全部的 Polyfill 都加載進來,致使產出文件過大。推薦使用 @babel/preset-envuseBuiltIns='usage' 方案,此配置項會根據瀏覽器的兼容性幫助咱們按需引入所需的墊片;此外咱們也可使用動態 polyfill 服務,每次根據瀏覽器的 User Agent,下發不一樣的 Polyfill,具體能夠參考 polyfill.io
  • 預加載資源 webpackPrefetch
使用 webpackPrefetch 來提早預加載一些資源,意思就是 未來可能須要一些模塊資源,在覈心代碼加載完成以後帶寬空閒的時候再去加載須要用到的模塊代碼。
  • icon 類圖片使用 css Sprite 來合併圖片
若是 icon 類圖片太多的話,就使用雪碧圖合成一張圖片,減小網絡請求,或者使用字體文件。
  • html-webpack-externals-plugin
此插件能夠將一些公用包提取出來使用 cdn 引入,不打入 bundle 中,從而減小打包文件大小,加快打包速度。
  • 合理配置 chunk 的哈希值
在生產環境打包,必定要配置文件的 hash,這樣有助於瀏覽器緩存咱們的文件,當咱們的代碼文件沒變化的時候,用戶就只須要讀取瀏覽器緩存的文件便可。 通常來講 javascript 文件使用 [chunkhash]css 文件使用 [contenthash]、其餘資源(例如圖片、字體等)使用 [hash]

更多性能優化方法筆者就再也不一一列舉了,由於關於 webpack 性能優化的方法實在是太多了,你們能夠根據實際遇到的問題制定相關的優化方案。

 

小結

今天這篇文章介紹了 webpack 打包的一些優化方案,從項目體積再到對項目速度,咱們提出了一些優化方案,你們能夠在具體的項目中去進行實踐。

固然我還要提一嘴,若是你的項目自己構建就比較快,那麼你其實就不須要使用文章中提到的方法去對項目進行優化,可能效果會拔苗助長。

對於文章中有些一筆帶過的內容,你們能夠在個人 webpack 學習整理文檔 找到相應的介紹。

實不相瞞,想要個贊!

 

相關連接

 

示例代碼

示例代碼能夠看這裏:

相關文章
相關標籤/搜索