webpack實(shí)戰(zhàn)——打包優(yōu)化【中】

前言

上篇從多線程打包縮小打包作用域兩個方面入手,對webpack打包層面做出優(yōu)化。本篇描述從動態(tài)鏈接庫思想方面繼續(xù)深入探究打包層面的深度優(yōu)化德迹。

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

動態(tài)鏈接庫(Dynamic Link Library 或者 Dynamic-link Library雀监,縮寫為 DLL),是微軟公司在微軟Windows操作系統(tǒng)中在塔,實(shí)現(xiàn)共享函數(shù)庫概念的一種方式幻件。這些庫函數(shù)的擴(kuò)展名是 ”.dll"、".ocx"(包含ActiveX控制的庫)或者 ".drv"(舊式的系統(tǒng)驅(qū)動程序)蛔溃。

當(dāng)一段相同的子程序被多個程序調(diào)用時绰沥,為了減少內(nèi)存消耗,可以將這段子程序存儲為一個可執(zhí)行文件贺待,當(dāng)被多個程序調(diào)用時只在內(nèi)存中生成和使用同一個實(shí)例徽曲。

今天要介紹的主角“DLLPlugin”則借鑒了動態(tài)鏈接庫的思路,對于第三方模塊或者一些不常變化的模塊預(yù)先進(jìn)行編譯和打包麸塞,然后再項(xiàng)目實(shí)際構(gòu)建過程中直接取用秃臣。不過區(qū)別還是有的,DLLPlugin實(shí)際生成的文件是JS文件而不是動態(tài)鏈接庫。在打包vendor的時候還會附加生成一份vendor的模塊清單奥此,這份清單將會在工程業(yè)務(wù)模塊打包時起到鏈接和索引的作用弧哎。

1 vendor配置

首先需要為動態(tài)鏈接庫單獨(dú)創(chuàng)建一個Webpack配置文件,例如:webpack.vendor.config.js稚虎,注意要與webpack.config.js區(qū)分開來撤嫩。

例:

// webpack.vendor.config.js
const path = require('path');
const webpack = require('webpack');
const dllAssetPath = path.join(__dirname, 'dll');
const dllLibraryName = 'dllExample';

module.exports = {
    entry: ['react'],
    output: {
        path: dllAssetPath,
        filename: 'vendor.js',
        library: dllLibraryName
    },
    plugins: [
        new webpack.DllPlugin({
            name: dllLibraryName,
            path: path.join(dllAssetPath, 'manifest.json')
        })
    ]
}

其中,entry指定了將哪些模塊打包為vendor蠢终,plugins的部分引入了DLLPlugin序攘,并有如下配置:

  • name: 導(dǎo)出的dll library的名字,需要與output.library的值對應(yīng)蜕径;
  • path: 資源清單的絕對路徑两踏,業(yè)務(wù)打包時將會使用這個清單進(jìn)行模塊索引;

2 vendor打包

接下來就要打包vendor并且生成資源清單兜喻。為后續(xù)方便操作梦染,可以在package.json中配置一條運(yùn)行指令:

// pachage.json
{
    ...
    "scripts": {
        ...
        "dll": "webpack --config webpack.vendor.config.js"
    }
}

然后執(zhí)行npm run dll,會發(fā)現(xiàn)生成了一個dll目錄朴皆,里面對應(yīng)有兩個文件:

  • vendor.js: 庫的代碼
  • manifest.json: 資源清單

感興趣的可以打開這兩個文件閱讀一下帕识。

3 鏈接到業(yè)務(wù)代碼

試過之后,我們就要考慮將vendor鏈接到項(xiàng)目中去了遂铡。這里推薦與DLLPlugin配套的插件“DLLReferencePlugin”肮疗,它起到索引和鏈接作用。在工程的webpack配置文件中(注意是webpack.config.js扒接,不是vendor的配置文件)伪货,通過DLLReferencePlugin來獲取剛才打包好的資源清單,然后在頁面中添加vendor.js就可以引用钾怔。

// webpack.cinfig.js
const path = require('path');
const webpack = require('webpack');
module.exports = {
    ...
    plugins: [
        new webpack.DllReferencePlugin({
            manifest: require(path.join(__dirname, 'dll/manifest.json')
        })
    ]
}

那么 index.html 引入會即可:

...

<body>
...

<script src="dll/vendor.js"></script>
<script src="dist/app.js"></script>
</body>

設(shè)置完畢后碱呼,當(dāng)頁面執(zhí)行到vendor.js時,會聲明全局變量dllExample宗侦,而manifest相當(dāng)于注入app.js的資源地圖愚臀,app.js會通過name字段找到名為DLLExample的library,再進(jìn)一步獲取其內(nèi)部模塊矾利。

4 潛在問題

細(xì)心的小伙伴或許已經(jīng)發(fā)現(xiàn)了姑裂,在當(dāng)前配置中會存在一個問題:當(dāng)打開manifest.json文件后,可以發(fā)現(xiàn)每個模塊都會有一個id男旗,其值是按照數(shù)字順序遞增的舶斧,而業(yè)務(wù)代碼在引用vendor中模塊時也是引用這個數(shù)字id,當(dāng)我們更vendor時這個數(shù)字id也會隨之發(fā)生改變剑肯。

現(xiàn)假設(shè)我們工程目錄中有如下資源文件捧毛,并每個資源都加上了chunk hash:

  • vendor@[hasn].js
  • pageUser@[hasn].js
  • pageIndex@[hasn].js
  • util@[hasn].js

現(xiàn)在vendor中you一些模塊,例如包含了react让网,其id為5.當(dāng)嘗試添加更多模塊到vendor中的時候呀忧,那么重新進(jìn)行Dll構(gòu)建時,moment.js可能出現(xiàn)在react之前溃睹,此時react的id會變?yōu)?.而pageUser和pageIndex是通過id進(jìn)行引用的而账,因此他們的文件內(nèi)容也發(fā)生了改變。此時我們會面臨如下情況:

  1. 兩個頁面的chunk hash均發(fā)生了改變因篇。這是我們不希望看到的泞辐,因?yàn)樗麄儽旧聿o變化,但是vendor的改變卻驅(qū)使用戶不得不重新下載所有資源竞滓。
  2. 兩個頁面chunk hash沒有改變咐吼,但是這種情況更為糟糕:vendor中的模塊id改變了,但是用戶沒有更新緩存商佑,使用的還是舊版本的內(nèi)容锯茄,而引用不到新的vendor模塊,導(dǎo)致頁面發(fā)生錯誤茶没。并且對于開發(fā)者而言肌幽,這個錯誤卻難以排查,因?yàn)殚_發(fā)環(huán)境下一切正常抓半!

針對上述的問題2喂急,解決方法是在打包vendor時添加上HashedModuleIdsPlugin,如下:

// webpack.vendor.config.js
module.exports = {
    ...
    plugins: [
        new webpack.DllPlugin({
            name: dllLibraryName,
            path: path.join(dllAssetPath, 'manifest.json')
        }),
        // 添加HashedModuleIdsPlugin
        new webpack.HashedModuleIdsPlugin();
    ]
}

HashedModuleIdsPlugin是webpack3中被引入進(jìn)來的笛求,主要就是為了解決數(shù)字id的問題廊移。HashedModuleIdsPlugin可以把id的生成算法修改為根據(jù)模塊的引用路徑生成一個字符串hash。

注:從webpack3開始探入,模塊id不僅可以是數(shù)字狡孔,也可以是字符串。

小結(jié)

本篇從動態(tài)鏈接庫思想著手新症,介紹了DLLPlugin與其配套插件DLLReferencePlugin使用步氏,將第三方庫與一些不常改動的模塊編譯打包,處理為類似于動態(tài)鏈接庫的JS文件徒爹,以此來節(jié)約服務(wù)器資源荚醒。
下一篇介紹打包優(yōu)化最后一個環(huán)節(jié):死代碼檢測與去除。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末隆嗅,一起剝皮案震驚了整個濱河市界阁,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌胖喳,老刑警劉巖泡躯,帶你破解...
    沈念sama閱讀 206,214評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異,居然都是意外死亡较剃,警方通過查閱死者的電腦和手機(jī)咕别,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,307評論 2 382
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來写穴,“玉大人惰拱,你說我怎么就攤上這事“∷停” “怎么了偿短?”我有些...
    開封第一講書人閱讀 152,543評論 0 341
  • 文/不壞的土叔 我叫張陵,是天一觀的道長馋没。 經(jīng)常有香客問我昔逗,道長,這世上最難降的妖魔是什么篷朵? 我笑而不...
    開封第一講書人閱讀 55,221評論 1 279
  • 正文 為了忘掉前任勾怒,我火速辦了婚禮,結(jié)果婚禮上款票,老公的妹妹穿的比我還像新娘控硼。我一直安慰自己,他們只是感情好艾少,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,224評論 5 371
  • 文/花漫 我一把揭開白布恃慧。 她就那樣靜靜地躺著频鉴,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上相恃,一...
    開封第一講書人閱讀 49,007評論 1 284
  • 那天吹散,我揣著相機(jī)與錄音丸升,去河邊找鬼界赔。 笑死,一個胖子當(dāng)著我的面吹牛雏吭,可吹牛的內(nèi)容都是我干的锁施。 我是一名探鬼主播,決...
    沈念sama閱讀 38,313評論 3 399
  • 文/蒼蘭香墨 我猛地睜開眼杖们,長吁一口氣:“原來是場噩夢啊……” “哼悉抵!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起摘完,我...
    開封第一講書人閱讀 36,956評論 0 259
  • 序言:老撾萬榮一對情侶失蹤姥饰,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后孝治,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體列粪,經(jīng)...
    沈念sama閱讀 43,441評論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡审磁,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,925評論 2 323
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了岂座。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片态蒂。...
    茶點(diǎn)故事閱讀 38,018評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖掺逼,靈堂內(nèi)的尸體忽然破棺而出吃媒,到底是詐尸還是另有隱情瓤介,我是刑警寧澤吕喘,帶...
    沈念sama閱讀 33,685評論 4 322
  • 正文 年R本政府宣布,位于F島的核電站刑桑,受9級特大地震影響氯质,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜祠斧,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,234評論 3 307
  • 文/蒙蒙 一闻察、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧琢锋,春花似錦辕漂、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,240評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至鲸阻,卻和暖如春跋涣,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背鸟悴。 一陣腳步聲響...
    開封第一講書人閱讀 31,464評論 1 261
  • 我被黑心中介騙來泰國打工陈辱, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人细诸。 一個月前我還...
    沈念sama閱讀 45,467評論 2 352
  • 正文 我出身青樓沛贪,卻偏偏與公主長得像,于是被迫代替她去往敵國和親震贵。 傳聞我的和親對象是個殘疾皇子利赋,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,762評論 2 345