(九)打包優(yōu)化

主要介紹一些優(yōu)化Webpack配置的方法仔引,目的是讓打包的速度更快坐求,輸出的資源更小絮重。首先重述一條軟件工程領(lǐng)域的經(jīng)驗——不要過早優(yōu)化惋啃,在項目的初期不要看到任何優(yōu)化點就拿來加到項目中碴开,這樣不但增加了復(fù)雜度毅该,優(yōu)化的效果也不會太理想博秫。一般是當(dāng)項目發(fā)展到一定規(guī)模后,性能問題隨之而來眶掌,這時再去分析然后對癥下藥挡育,才有可能達到理想的優(yōu)化效果。

HappyPack

HappyPack是一個通過多線程來提升Webpack打包速度的工具朴爬。我們可以猜測HappyPack這個名字的由來即寒,也許是它的作者在使用Webpack過程中無法忍受其漫長的打包過程,于是自己寫了一個插件讓速度快了很多召噩,擺脫了構(gòu)建的痛苦母赵。對于很多大中型工程而言,HappyPack確實可以顯著地縮短打包時間具滴。首先讓我們了解一下它是如何工作的凹嘲。
實際使用時,要用HappyPack提供的loader來替換原有l(wèi)oader构韵,并將原有的那個通過HappyPack插件傳進去周蹭。請看下面的例子:

//初始化webpack配置(使用Happpack前)
module.exports = {
   //...
module:{
 rules:[
          {
             test:'/\.js$/',
             exclude:/node_modules/,
             loader:babel-loader,
             options:presets:['react']}] 
           }]
}

//使用HappyPack的配置
const HappyPack = require('happpack');
module.exports = {
//...
module:{
 rules:[
          {
             test:'/\.js$/',
             exclude:/node_modules/,
             loader:'happyPack/loader',
           }]
},
plugins:[
  new HappyPack({
      loaders:[
          {
              loaders:'babel-loader',
              options:{ presets:['react']}
           }
     ]

  })
 ]
}
//在module.rules中疲恢,我們使用happypack/loader替換了原有的babel-loader,并在plugins中添加了HappyPack的插件棚愤,將原有的babel-loader連同它的配置插入進去即可萎攒。

多個loader

多個loader的優(yōu)化在使用HappyPack優(yōu)化多個loader時矛绘,需要為每一個loader配置一個id货矮,否則HappyPack無法知道rules與plugins如何一一對應(yīng)羊精。請看下面的例子,這里同時對babel-loader和ts-loader進行了Happypack的替換喧锦。

//初始化webpack配置(使用Happpack前)
module.exports = {
   //...
module:{
 rules:[
          {
             test:'/\.js$/',
             exclude:/node_modules/,
             loader:'happyPack/loader?id=js',
           },
          {
             test:'/\.ts$/',
             exclude:/node_modules/,
             loader:'happyPack/loader?id=ts',
           }
           ]
},
plugins:[
  new HappyPack({
     id:'js',
      loaders:[
          {
              loaders:'babel-loader',
              options:{ presets:['react']}
           }
     ]
  }),
new HappyPack({
     id:'ts',
      loaders:[
          {
              loaders:'ts-loader',
              options:{ }
           }
     ]

  }),
 ]

}

在使用多個HappyPack loader的同時也就意味著要插入多個HappyPack的插件燃少,每個插件加上id來作為標識铃在。同時我們也可以為每個插件設(shè)置具體不同的配置項碍遍,如使用的線程數(shù)怕敬、是否開啟debug模式等帘皿。

縮小打包作用域

從宏觀角度來看,提升性能的方法無非兩種:增加資源或者縮小范圍虽填。增加資源就是指使用更多CPU和內(nèi)存曹动,用更多的計算能力來縮短執(zhí)行任務(wù)的時間仁期;縮小范圍則是針對任務(wù)本身,比如去掉冗余的流程熬的,盡量不做重復(fù)性的工作等赊级。前面我們說的HappyPack屬于增加資源,那么接下來我們再談?wù)勅绾慰s小范圍橡伞。

  1. exclude和include
module:{
   rules:[
          {
             test:'/\.js$/',
             include:/src\/scripts/,
              loader:'babel-loader'
           }
   ]
 }
  1. noParse

有些庫我們是希望Webpack完全不要去進行解析的兑徘,即不希望應(yīng)用任何loader規(guī)則羡洛,庫的內(nèi)部也不會有對其他模塊的依賴,那么這時可以使用noParse對其進行忽略崭闲。請看下面的例子:


module.exports={
  //...
  module:{
  npParse:/lodash/
   }
}


上面的配置將會忽略所有文件名中包含lodash的模塊威蕉,這些模塊仍然會 <被打包進資源文件>,只不過Webpack不會對其進行任何解析牍戚。

  1. IgnorePlugin

exclude和include是確定loader的規(guī)則范圍,noParse是不去解析但仍會打包到bundle中鼎天。最后讓我們再看一個插件IgnorePlugin暑竟,它可以完全排除一些模塊,被排除的模塊即便被引用了 <也不會被打包> 進資源文件中罗岖。

4)Cache
有些loader會有一個cache配置項腹躁,用來在編譯代碼后同時保存一份緩存纺非,在執(zhí)行下一次編譯前會先檢查源碼文件是否有變化,如果沒有就直接采用緩存弱左,也就是上次編譯的結(jié)果炕淮。這樣相當(dāng)于實際編譯的只有變化了的文件,整體速度上會有一定提升们镜。

動態(tài)鏈接庫與DllPlugin

DllPlugin和Code Splitting有點類似模狭,都可以用來提取公共模塊卡辰,但本質(zhì)上有一些區(qū)別邪意。Code Splitting的思路是設(shè)置一些特定的規(guī)則并在打包的過程中根據(jù)這些規(guī)則提取模塊雾鬼;DllPlugin則是將vendor完全拆出來,有自己的一整套Webpack配置并獨立打包晶疼,在實際工程構(gòu)建時就不用再對它進行任何處理,直接取用即可锭吨。因此零如,理論上來說锄弱,DllPlugin會比Code Splitting在打包速度上更勝一籌,但也相應(yīng)地增加了配置肖卧,以及資源管理的復(fù)雜度塞帐。下面我們一步步來進行DllPlugin的配置壁榕。

1) DllPlugin vendor配置
首先需要為動態(tài)鏈接庫單獨創(chuàng)建一個Webpack配置文件赎瞎,比如命名為webpack.vendor.config.js务甥,用來區(qū)別工程本身的配置文件webpack.config.js。

//webpack.confing.js
const path = require('path')
const webpack = require('webpack')
const dllAssetPath = path.join(__dirname,'dll');
//動態(tài)鏈接庫的 名稱
const dllLibraryName = ‘dllExample’;

module.exports = {
  entry:['react'],
  output:{
       path:dllAssetPath ,
       filename:'vendor.js',
        library:dllLibraryName 
   },
   plugins:[  new webpack.DllPlugin({  
//導(dǎo)出的dll library的名字敞临,它需要與output.library的值對應(yīng)态辛。
                        name:dllLibraryName,
//path:資源清單的絕對路徑,業(yè)務(wù)代碼打包時將會使用這個清單進行模塊索引
                         path:path.join(dllAssetPath ,'manifest.json')
                   })]
}

2)vendor打包.
打包后會生成一個dll目錄挺尿,里面有兩個文件vendor.js和manifest.json奏黑,前者包含了庫的代碼,后者則是資源清單编矾。
可以預(yù)覽一下生成的vendor.js熟史,它以一個立即執(zhí)行函數(shù)表達式的聲明開始蹂匹。

var dllExample = (function(params){
//...

})(params)

manifest.json,其大體內(nèi)容如下:

{
  "name":"dllExample",
   "content":{
          "./node_modules/xxx.js":{
                "id":0,
                  "buidMeta":{"providedExports":true}
              }
     }
}
})(params)
  1. 鏈接到業(yè)務(wù)代碼
    將vendor鏈接到項目中很簡單凹蜈,這里我們將使用與DllPlugin配套的插件DllReferencePlugin限寞,它起到一個索引和鏈接的作用。在工程的webpack配置文件(webpack.config.js)中计雌,通過DllReferencePlugin來獲取剛剛打包好的資源清單白粉,然后在頁面中添加vendor.js的引用就可以了
//webpack.config.js
module:{
  rules:[
    new webpack.DllReferencePlugin({manifest:require(path.join(__dirname,'dll/manifest.json))})
]
}

//index.html
<body>
   <script src="dlll/vendor.js"></script>
   <script src="dist/app.js"></script>
</body>

app.js會先通過name字段找到名為dllExample的library,再進一步獲取其內(nèi)部模塊鹃祖。這就是我們在webpack.vendor.config.js中給DllPlugin的name和output.library賦相同值的原因。

tree shaking

ES6 Module依賴關(guān)系的構(gòu)建是在代碼編譯時而非運行時祖能。基于這項特性Webpack提供了tree shaking功能戒悠,它可以在打包過程中幫助我們檢測工程中沒有被引用過的模塊轧膘,這部分代碼將永遠無法被執(zhí)行到钞螟,因此也被稱為“死代碼”。Webpack會對這部分代碼進行標記鳞滨,并在資源壓縮時將它們從最終的bundle中去掉。下面的例子簡單展示了tree shaking是如何工作的熔任。

//index.js
import { foo } from './util.js'
foo()
//util.js
export function foo (){}

//沒有被任何其他模塊引用。屬于死代碼
export function bar(){}

ES6 Module

tree shaking只能對ES6 Module生效笋敞。有時我們會發(fā)現(xiàn)雖然只引用了某個庫中的一個接口,卻把整個庫加載進來了夯巷,而bundle的體積并沒有因為tree shaking而減小。這可能是由于該庫是使用CommonJS的形式導(dǎo)出的趁餐,為了獲得更好的兼容性,目前大部分的npm包還在使用CommonJS的形式后雷。也有一些npm包同時提供了ES6 Module和CommonJS兩種形式導(dǎo)出,我們應(yīng)該盡可能使用ES6 Module形式的模塊臀突,這樣tree shaking

Webpack進行依賴關(guān)系構(gòu)建

如果我們在工程中使用了babel-loader,那么一定要通過配置來禁用它的模塊依賴解析候学。因為如果由babel-loader來做依賴解析,Webpack接收到的就都是轉(zhuǎn)化過的CommonJS形式的模塊梳码,無法進行tree-shaking隐圾。禁用babel-loader模塊依賴解析的配置示例如下:

module.exports = {
   module:{
       rules:[
              {
                  test:'/\.js$/',
                   exclude:/node_module/,
                   loader:'babel-loader',
                   options:{
                          presets:[
                                //這里一定要加上 modules:false
                                 [@babel/preset-env,{module:false}]
                            ]
                   }
               }
      ]
     }

}

使用壓縮工具去除死代碼tree shaking

本身只是為死代碼添加上標記暇藏,真正去除死代碼是通過壓縮工具來進行的。使用我們前面介紹過的terser-webpack-plugin即可盐碱。在Webpack 4之后的版本中,將mode設(shè)置為production也可以達到相同的效果

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末甸各,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子趣倾,更是在濱河造成了極大的恐慌,老刑警劉巖儒恋,帶你破解...
    沈念sama閱讀 217,826評論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件黔漂,死亡現(xiàn)場離奇詭異诫尽,居然都是意外死亡炬守,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,968評論 3 395
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來曹洽,“玉大人,你說我怎么就攤上這事送淆。” “怎么了怕轿?”我有些...
    開封第一講書人閱讀 164,234評論 0 354
  • 文/不壞的土叔 我叫張陵,是天一觀的道長撞羽。 經(jīng)常有香客問我,道長诀紊,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,562評論 1 293
  • 正文 為了忘掉前任渡紫,我火速辦了婚禮,結(jié)果婚禮上惕澎,老公的妹妹穿的比我還像新娘。我一直安慰自己唧喉,他們只是感情好捣卤,可當(dāng)我...
    茶點故事閱讀 67,611評論 6 392
  • 文/花漫 我一把揭開白布八孝。 她就那樣靜靜地躺著,像睡著了一般干跛。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上楼入,一...
    開封第一講書人閱讀 51,482評論 1 302
  • 那天,我揣著相機與錄音嘉熊,去河邊找鬼。 笑死阐肤,一個胖子當(dāng)著我的面吹牛讲坎,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播愧薛,決...
    沈念sama閱讀 40,271評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼厚满!你這毒婦竟也來了碧磅?” 一聲冷哼從身側(cè)響起碘箍,我...
    開封第一講書人閱讀 39,166評論 0 276
  • 序言:老撾萬榮一對情侶失蹤丰榴,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后秆撮,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,608評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡职辨,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,814評論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了舒裤。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 39,926評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡腾供,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出伴鳖,到底是詐尸還是另有隱情,我是刑警寧澤榜聂,帶...
    沈念sama閱讀 35,644評論 5 346
  • 正文 年R本政府宣布,位于F島的核電站须肆,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏休吠。R本人自食惡果不足惜扳埂,卻給世界環(huán)境...
    茶點故事閱讀 41,249評論 3 329
  • 文/蒙蒙 一瘤礁、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦岩调、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,866評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽葱淳。三九已至,卻和暖如春赞厕,著一層夾襖步出監(jiān)牢的瞬間艳狐,已是汗流浹背皿桑。 一陣腳步聲響...
    開封第一講書人閱讀 32,991評論 1 269
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留诲侮,地道東北人。 一個月前我還...
    沈念sama閱讀 48,063評論 3 370
  • 正文 我出身青樓沟绪,卻偏偏與公主長得像,于是被迫代替她去往敵國和親近零。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,871評論 2 354