前言
本篇介紹一些webpack優化的配置方法,目的有二:
- 打包速度更快
- 輸出資源更小
注意:在軟件工程領域有一條十分重要的功能經驗,不要過早優化。在項目初期不要看到一個可以優化的點就去做優化,這樣極有可能會增加尤其開發及維護的複雜度,並且從整體效果看,優化效果不會太理想。
1. HappyPack
HappyPack是一個通過多線程來提升webpack打包速度的工具
1.1 工作原理
在打工過程中,非常耗時的一個工作是使用loader將各種資源進行轉譯處理,例如常見的使用babel-loader將ES6+語法代碼轉譯爲ES5等。代碼轉移的工作流程如下:
- 從配置中獲取打包入口;
- 匹配loader規則,並對入口模塊進行轉譯;
- 對轉譯後的模塊進行依賴查找;
- 對新找到的模塊重複步驟2)和3),直到沒有新的依賴模塊。
從上述步驟中可以看出,從步驟2)到步驟4)其實是一個遞歸的過程,webpack需要一步步地獲取更下一級的資源然後逐個進行轉譯。爲什麼逐個?問題就在於webpack是單線程的。而HappyPack便將這裏作爲切入口,它的核心特性是可以開啓多個線程,並行的對不同模塊進行轉譯,這樣便更加充分的利用計算機資源來提升打包速度。
1.2 工程目標
HappyPack顯然更加適用於轉譯任務比較繁重的工程,當我們把類似babel-loadre,ts-loader等遷移到HappyPack之上後,一般會有比較不錯的效果,而對於sass-loader等本身消耗時間並不太多的功能則效果一般。
1.3 單個loader優化
以babel-loader爲例:
// 初始webpack.config.js
module.exports = {
// ...
module: {
rules: [
{
test: /\.js$/,
exclude: /node_modules/,
loader: 'babel-loader',
options: {
presets: ['react'],
}
}
]
}
}
使用HappyPack:
// 使用HappyPack:
const HappyPack = require('happypack');
module.exports = {
// ...
module: {
rules: [
{
test: /\.js$/,
exclude: /node_modules/,
loader: 'happypack/loader'
}
]
},
plugin: [
new HappyPack({
loaders: [
{
loader: 'babel-loader',
options: {
presets: ['react']
}
}
]
})
]
}
在module.rules中,我們使用happypack/loader替換掉了原來的babel-loader,而在plugins中添加了HappyPack插件,將原來的bebel-loader及其配置插入進去即可。
1.4 多個loader優化
在使用HappyPack對多個loader進行優化時,需要爲每一個loader配置一個id,否則HappyPack無法知道rules與plugins的對應關係。
這裏以babel-loader及ts-loader爲例:
// webpack.config.js
const HappyPack = require('happypack');
module.exports = {
// ...
module: {
rules: [
{
test: /\.js$/,
exclude: /node_modules/,
loader: 'happypack/loader?id=js'
},
{
test: /\.ts$/,
exclude: /node_modules/,
loader: 'happypack/loader?id=ts'
}
]
},
plugin: [
new HappyPack({
id: 'js',
loaders: [
{
loader: 'babel-loader',
options: {
// babel配置
}
}
]
}),
new HappyPack({
id: 'ts',
loaders: [
{
loader: 'ts-loader',
options: {
// ts配置
}
}
]
})
]
}
除上述配置之外,可以查閱官方文檔查看更多的配置,例如線程數,debug模式等。
2. 縮小打包作用域
從宏觀角度看,提升性能的方式總結爲兩種:
- 增加資源: 使用更多的CPU和內存,用更多的計算能力來縮短任務執行時間;
- 縮小範圍: 針對任務本身,去除冗餘流程,不做重複性工作或使其簡單化;
而上面我們所瞭解的HappyPack明顯屬於增加資源,那接下來介紹從縮小範圍的幾個方案。
2.1 exclude 和 include
在前面章節(預處理器(loader)【上篇】)中,介紹過exclude和include,在配置loader的時候一般都會對其進行配置。對於JS來說,一般需要把node_modules目錄排除掉,另外當exclude和include規則有重疊部分時,exclude優先級更高。
那對於此處,我們使用include讓babel-loader只生效於源碼目錄:
// webpack.config.js
...
module: {
rules: [
test: /\.js$/,
include: /src\/scripts/,
loader: 'babel-loader'
]
}
2.2 noParse
有些庫我們希望webpack完全不要去解析,那此時可以使用noParse對其進行忽略,如:
// webpack.config.js
// ...
module.exports = {
noParse: {
// fullPath是絕對路徑,如 /User/me/app/webpack/noparse/lib/lodash.js
return /lib/.test(fullPath);
}
}
如上配置將忽略所有lib目錄下的資源解析。
2.3 IgnorePlugin
exclude和include是確定loader的規則範圍,noParse是不去解析但仍會打包到bundle中,那接下來介紹一個插件——IgnorePlugin,他可以完全排除一些模塊,被排除的模塊即使被引用也不會被打包進資源文件中。一般作用於排除一些庫相關文件。
一個由庫產生的額外資源我們用不到但沒辦法去掉時,可以考慮使用此方法處理。
例,一個日期時間處理的相關插件Moment.js,爲了做本地化會加載許多語言包,但一般我們只會用到本地的語言包而不會使用其他地區的語言包,而語言包會佔用很大體積,這時可以使用IgnorePlugin來做處理:
// webpack.config.js
...
plugins: [
new webpack.IgnorePlugin({
resourceRegexp: /^\.\/locale$/, // 匹配資源文件
contextRegExp: /moment$/ // 匹配檢索目錄
})
]
2.4 Cache
我們在使用某些loader時會有一個cache的配置項,用來在編譯代碼後同時保存一份緩存,在執行下一次編譯前會優先檢查源碼文件是否有變化,沒有則直接使用緩存結果,也就是上次編譯的結果。這樣一來,只有在發生變化時編譯變化了的文件,對於整體而言也屬於一種優化處理。
小結
本篇從多線程打包
和縮小打包作用域
兩個方面入手,對webpack打包層面做出優化。下一篇描述從動態鏈接庫思想
與死代碼檢測
方面繼續深入探究打包層面的深度優化。