webpack4 SplitChunks實(shí)現(xiàn)代碼分隔詳解

代碼均放在git倉庫

Webpack 4給我們帶來了一些改變抬闯。包括更快的打包速度预侯,引入了SplitChunksPlugin插件來取代(之前版本里的)CommonsChunksPlugin插件嫂拴。在這篇文章中璃饱,你將學(xué)習(xí)如何分割你的輸出代碼榛丢,從而提升我們應(yīng)用的性能。

SplitChunks插件(webpack 4.x以前使用CommonsChunkPlugin)允許我們將公共依賴項(xiàng)提取到現(xiàn)有的entry chunk或全新的代碼塊中贺奠。

代碼分割的理念

首先搞明白: webpack里的代碼分割是個(gè)什么鬼哩牍? 它允許你將一個(gè)文件分割成多個(gè)文件。如果使用的好倘待,它能大幅提升你的應(yīng)用的性能疮跑。其原因是基于瀏覽器會緩存你的代碼這一事實(shí)。每當(dāng)你對某一文件做點(diǎn)改變凸舵,訪問你站點(diǎn)的人們就要重新下載它祖娘。然而依賴卻很少變動(dòng)。如果你將(這些依賴)分離成單獨(dú)的文件啊奄,訪問者就無需多次重復(fù)下載它們了渐苏。

使用webpack生成一個(gè)或多個(gè)包含你源代碼最終版本的“打包好的文件”(bundles),(概念上我們當(dāng)作)它們由(一個(gè)一個(gè)的)chunks組成菇夸。

首先 webpack 總共提供了三種辦法來實(shí)現(xiàn) Code Splitting琼富,如下:

  • 入口配置:entry 入口使用多個(gè)入口文件;
  • 抽取公有代碼:使用 SplitChunks 抽取公有代碼庄新;
  • 動(dòng)態(tài)加載 :動(dòng)態(tài)加載一些代碼鞠眉。

這里我們姑且只討論使用 SplitChunks 抽取公有代碼。

splitChunks配置

在src目錄下創(chuàng)建三個(gè)文件pageA.js择诈、pageB.js和pageC.js械蹋。代碼詳情見文章開頭git倉庫。

// src/pageA.js
var react = require('react');
var reactDom = require('react-dom');
var utility1 = require('../utils/utility1');
var utility2 = require('../utils/utility2');
new Vue();

module.exports = "pageA";
// src/pageB.js
var react = require('react');
var reactDom = require('react-dom');
var utility2 = require('../utils/utility2');
var utility3 = require('../utils/utility3');

module.exports = "pageB";
// src/pageC.js
var react = require('react');
var reactDom = require('react-dom');
var utility2 = require('../utils/utility2');
var utility3 = require('../utils/utility3');

module.exports = "pageC";

入口文件 && 出口文件

entry: {
    pageA: "./src/pageA",    // 引用utility1.js  utility2.js
    pageB: "./src/pageB",    // 引用utility2.js  utility3.js
    pageC: "./src/pageC",   // 引用utility2.js  utility3.js
},
output: {
    path: path.join(__dirname, "dist"),
    filename: "[name].[hash:8].bundle.js"
},

配置optimization

首先我們配置optimization如下:

optimization: {
    splitChunks: {
      chunks: "all",
  },

執(zhí)行npm run build打包命令之后羞芍,查看dist目錄

image.png

可以發(fā)現(xiàn)哗戈,打包出來的除了三個(gè)page文件,還存在一個(gè)vendors~pageA~pageB~pageC.[hash].bundle.js文件(此文件中保存了pageA涩金、pageB谱醇、pageC和node_modules中共有的size大于30KB的文件)暇仲。事實(shí)上這全靠了配置中本身默認(rèn)固有一個(gè)cacheGroups的配置項(xiàng):

splitChunks: {
    chunks: "all",
    cacheGroups: {
      vendors: {
        test: /[\\/]node_modules[\\/]/,  // 匹配node_modules目錄下的文件
        priority: -10   // 優(yōu)先級配置項(xiàng)
      },
      default: {
        minChunks: 2,
        priority: -20,   // 優(yōu)先級配置項(xiàng)
        reuseExistingChunk: true
      }
    }
  }

在默認(rèn)設(shè)置中步做,會將 node_mudules 文件夾中的模塊打包進(jìn)一個(gè)叫 vendors的bundle中副渴,所有引用超過兩次的模塊分配到 default bundle 中。更可以通過 priority 來設(shè)置優(yōu)先級全度。

參數(shù)說明如下:

  • chunks:表示從哪些chunks里面抽取代碼煮剧,除了三個(gè)可選字符串值 initial、async将鸵、all 之外勉盅,還可以通過函數(shù)來過濾所需的 chunks;
  • minSize:表示抽取出來的文件在壓縮前的最小大小顶掉,默認(rèn)為 30000草娜;
  • maxSize:表示抽取出來的文件在壓縮前的最大大小,默認(rèn)為 0痒筒,表示不限制最大大性兹颉;
  • minChunks:表示被引用次數(shù)簿透,默認(rèn)為1移袍;上述配置commons中minChunks為2,表示將被多次引用的代碼抽離成commons老充。

值得注意的是葡盗,如果沒有修改minSize屬性的話,而且被公用的代碼(假設(shè)是utilities.js)size小于30KB的話啡浊,它就不會分割成一個(gè)單獨(dú)的文件觅够。在真實(shí)情形下,這是合理的巷嚣,因?yàn)椋ㄈ绶指睿┎⒉荒軒硇阅艽_實(shí)的提升蔚约,反而使得瀏覽器多了一次對utilities.js的請求,而這個(gè)utilities.js又是如此之型孔选(不劃算)苹祟。

  • maxAsyncRequests:最大的按需(異步)加載次數(shù),默認(rèn)為 5评雌;
  • maxInitialRequests:最大的初始化加載次數(shù)树枫,默認(rèn)為 3;
  • automaticNameDelimiter:抽取出來的文件的自動(dòng)生成名字的分割符景东,默認(rèn)為 ~砂轻;
  • name:抽取出來文件的名字,默認(rèn)為 true斤吐,表示自動(dòng)生成文件名搔涝;
  • cacheGroups: 緩存組厨喂。(這才是配置的關(guān)鍵)

緩存組會繼承splitChunks的配置,但是test庄呈、priorty和reuseExistingChunk只能用于配置緩存組蜕煌。cacheGroups是一個(gè)對象,按上述介紹的鍵值對方式來配置即可诬留,值代表對應(yīng)的選項(xiàng)斜纪。除此之外,所有上面列出的選擇都是可以用在緩存組里的:chunks, minSize, minChunks, maxAsyncRequests, maxInitialRequests, name文兑『懈眨可以通過optimization.splitChunks.cacheGroups.default: false禁用default緩存組。默認(rèn)緩存組的優(yōu)先級(priotity)是負(fù)數(shù)绿贞,因此所有自定義緩存組都可以有比它更高優(yōu)先級(譯注:更高優(yōu)先級的緩存組可以優(yōu)先打包所選擇的模塊)(默認(rèn)自定義緩存組優(yōu)先級為0)

現(xiàn)在我們再重新來看一下pageA因块、pageB、pageC三個(gè)js文件籍铁,這三個(gè)文件中都引入了utility2.js文件涡上,但是此文件size很明顯小于30KB,所以這部分公用代碼并沒有分割出來寨辩。如果想要分割出來很簡單吓懈,只需要:

optimization: {
    splitChunks: {
      chunks: "all",
      minSize: 0
  },

執(zhí)行npm run build打包命令之后,查看dist目錄

image.png

顯然多了一個(gè)pageA~pageB~pageC.[hash].bundle.js文件靡狞。查看文件可得知此文件中存儲了utility2.js中的代碼耻警。如下圖所示(借助于webpack-bundle-analyzer插件,詳情文章末尾附錄)甸怕。

image.png

上圖可以看出甘穿,React相關(guān)代碼均放在了vendors~pageA~pageB~pageC.[hash].bundle.js文件中,如果我們想要抽離出React代碼梢杭,應(yīng)該怎么做吶温兼?

splitChunks: {
      chunks: "all",
      cacheGroups: {
        commons: {
          chunks: "initial",
          minChunks: 2,
          name: "commons",
          maxInitialRequests: 5,
          minSize: 0, // 默認(rèn)是30kb,minSize設(shè)置為0之后
                            // 多次引用的utility1.js和utility2.js會被壓縮到commons中
        },
        reactBase: {
          test: (module) => {
            return /react|redux|prop-types/.test(module.context);
          }, // 直接使用 test 來做路徑匹配武契,抽離react相關(guān)代碼
          chunks: "initial",
          name: "reactBase",
          priority: 10,
        }
      }
    },

run build之后如下圖所示募判。

image.png

看似非常完美,但是reactBase文件中竟然包含了node_modules咒唆,神奇的問題届垫?室友都睡覺了,這鍵盤聲影響不好全释,明天接著看装处。

附錄

我們再安裝一個(gè) webpack-bundle-analyzer,這個(gè)插件會清晰的展示出打包后的各個(gè)bundle所依賴的模塊:

npm i webpack-bundle-analyzer -D

引入:

const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin

使用浸船,在plugins數(shù)組中添加即可:

new BundleAnalyzerPlugin()
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末妄迁,一起剝皮案震驚了整個(gè)濱河市寝蹈,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌登淘,老刑警劉巖箫老,帶你破解...
    沈念sama閱讀 218,858評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異形帮,居然都是意外死亡槽惫,警方通過查閱死者的電腦和手機(jī)周叮,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,372評論 3 395
  • 文/潘曉璐 我一進(jìn)店門辩撑,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人仿耽,你說我怎么就攤上這事合冀。” “怎么了项贺?”我有些...
    開封第一講書人閱讀 165,282評論 0 356
  • 文/不壞的土叔 我叫張陵君躺,是天一觀的道長。 經(jīng)常有香客問我开缎,道長棕叫,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,842評論 1 295
  • 正文 為了忘掉前任奕删,我火速辦了婚禮俺泣,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘完残。我一直安慰自己伏钠,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,857評論 6 392
  • 文/花漫 我一把揭開白布谨设。 她就那樣靜靜地躺著熟掂,像睡著了一般。 火紅的嫁衣襯著肌膚如雪扎拣。 梳的紋絲不亂的頭發(fā)上赴肚,一...
    開封第一講書人閱讀 51,679評論 1 305
  • 那天,我揣著相機(jī)與錄音二蓝,去河邊找鬼誉券。 笑死,一個(gè)胖子當(dāng)著我的面吹牛侣夷,可吹牛的內(nèi)容都是我干的横朋。 我是一名探鬼主播,決...
    沈念sama閱讀 40,406評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼百拓,長吁一口氣:“原來是場噩夢啊……” “哼琴锭!你這毒婦竟也來了晰甚?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,311評論 0 276
  • 序言:老撾萬榮一對情侶失蹤决帖,失蹤者是張志新(化名)和其女友劉穎厕九,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體地回,經(jīng)...
    沈念sama閱讀 45,767評論 1 315
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡扁远,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,945評論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了刻像。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片畅买。...
    茶點(diǎn)故事閱讀 40,090評論 1 350
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖细睡,靈堂內(nèi)的尸體忽然破棺而出谷羞,到底是詐尸還是另有隱情,我是刑警寧澤溜徙,帶...
    沈念sama閱讀 35,785評論 5 346
  • 正文 年R本政府宣布湃缎,位于F島的核電站,受9級特大地震影響蠢壹,放射性物質(zhì)發(fā)生泄漏嗓违。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,420評論 3 331
  • 文/蒙蒙 一图贸、第九天 我趴在偏房一處隱蔽的房頂上張望蹂季。 院中可真熱鬧,春花似錦求妹、人聲如沸乏盐。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,988評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽父能。三九已至,卻和暖如春净神,著一層夾襖步出監(jiān)牢的瞬間何吝,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,101評論 1 271
  • 我被黑心中介騙來泰國打工鹃唯, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留爱榕,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,298評論 3 372
  • 正文 我出身青樓坡慌,卻偏偏與公主長得像黔酥,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,033評論 2 355

推薦閱讀更多精彩內(nèi)容