本文是我在使用 Webpack4 過程中的一些總結,介紹一些優化 Webpack 構建速度的方法。
Webpack 是當前最流行的打包工具,當前的最新版本是 Webpack4+,性能有了很大的提升,本文是我在使用過程中的一些總結,主要是提高 Webpack 的構建性能。
保持版本更新#
使用最新的 webpack 版本。新版本一般會進行性能優化。
保持最新的 Node.js 版本也能夠提升性能。除此之外,保證你的包管理工具 (例如 npm 或者 yarn) 為最新也能提升性能。較新的版本能夠建立更高效的模塊依賴樹以及提高解析速度。
在 Loader 中使用 include 或者 exclude#
配置 loader 的時候,使用 include,可以更精確指定要處理的目錄,可以減少不必要的遍歷,從而減少性能損失。同樣,對於已經明確知道的,不需要處理的目錄,則應該予以排除,從而進一步提升性能。故而,合理的設置 include 和 exclude,將會極大地提升 Webpack 打包優化速度。
module.exports = {
//...
module: {
rules: [
{
test: /\.js$/,
include: path.resolve(__dirname, 'src'),
loader: 'babel-loader'
}
]
}
};
提高解析速度#
以下幾步可以提高解析速度:
- 盡量減少 resolve.modules, resolve.extensions, resolve.mainFiles, resolve.descriptionFiles 中類目的數量,因為他們會增加文件系統調用的次數。
- 如果你不使用 symlinks ,可以設置 resolve.symlinks: false (例如 npm link 或者 yarn link).
- 如果你使用自定義解析 plugins ,並且沒有指定 context 資訊,可以設置 resolve.cacheWithContext: false 。
module.noParse#
防止 webpack 解析那些任何與給定正則表達式相匹配的文件。忽略的文件中不應該含有 import, require, define 的調用,或任何其他導入機制。忽略大型的類庫 可以提高構建性能。
module.exports = {
//...
module: {
noParse: /jquery|lodash/,
// 從 webpack 3.0.0 開始
noParse: function(content) {
return /jquery|lodash/.test(content);
}
}
};
babel-loader#
babel-loader 構建很慢,不僅要使用 exclude、include,盡可能準確的指定要編譯內容的目錄,而且要充分利用緩存,進一步提升性能。babel-loader 提供了 cacheDirectory 特定選項(默認 false),當有設置時,指定的目錄將用來緩存 loader 的執行結果。之後的 webpack 構建,將會嘗試讀取緩存,來避免在每次執行時,可能產生的、高性能消耗的 Babel 重新編譯過程。
通過使用 cacheDirectory 選項,將 babel-loader 提速至少兩倍。 這會將轉譯的結果緩存到文件系統中。
babel 在每個文件都插入了輔助代碼,使代碼體積過大!
babel 對一些公共方法使用了非常小的輔助代碼,比如 _extend。 默認情況下會被添加到每一個需要它的文件中
你可以引入 babel runtime 作為一個獨立模塊,來避免重複引入。
下面的配置禁用了 babel 自動對每個文件的 runtime 注入,而是引入 babel-plugin-transform-runtime 並且使所有輔助代碼從這裡引用。
rules: [
// 'transform-runtime' 插件告訴 babel 要引用 runtime 來代替注入。
{
test: /\.js$/,
exclude: /(node_modules|bower_components)/,
use: {
loader: 'babel-loader',
options: {
presets: ['@babel/preset-env'],
plugins: ['@babel/transform-runtime']
}
}
}
]
Dlls#
使用 DllPlugin 將更改不頻繁的代碼進行單獨編譯。這將改善引用程序的編譯速度,即使它增加了構建過程的複雜性。
Happypack 多進程構建#
由於 Node.js 的單進程限制,所有的 loader 雖然以 async 的形式來並發調用,但是還是運行在單個 node 的進程,以及在同一個事件循環中,這就直接導致了些問題:當同時讀取多個 loader 文件資源時,比如 babel-loader 需要 transform 各種 jsx,es6 的資源文件。在這種 CPU 密集型的場景下,Node 的單進程模型就沒有優勢了,而 Happypack 就是為解決此類問題而生。
Happypack 的處理思路是:將原有的 webpack 對 loader 的執行過程,從單一進程的形式擴展多進程模式,從而加速代碼構建;原本的流程保持不變,這樣可以在不修改原有配置的基礎上,來完成對編譯過程的優化,具體配置如下:
// 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
})
];
Happypack 在編譯過程中,除了利用多進程的模式加速編譯,還同時開啟了 cache 計算,能充分利用緩存讀取構建文件,對構建的速度提升也是非常明顯的