成人免费xxxxx在线视频软件_久久精品久久久_亚洲国产精品久久久_天天色天天色_亚洲人成一区_欧美一级欧美三级在线观看

「Webpack」從0到1學(xué)會(huì) Code Splitting

開發(fā) 前端
在默認(rèn)的配置情況下,我們知道,webpack 會(huì)把所有代碼打包到一個(gè) chunk 中,舉個(gè)例子當(dāng)你的一個(gè)單頁面應(yīng)用很大的時(shí)候,你可能就需要將每個(gè)路由拆分到一個(gè) chunk 中,這樣才方便我們實(shí)現(xiàn)按需加載。

[[408514]]

本文轉(zhuǎn)載自微信公眾號(hào)「微醫(yī)大前端技術(shù)」,作者焦傳鍇。轉(zhuǎn)載本文請(qǐng)聯(lián)系微醫(yī)大前端技術(shù)公眾號(hào)。

一、前言

在默認(rèn)的配置情況下,我們知道,webpack 會(huì)把所有代碼打包到一個(gè) chunk 中,舉個(gè)例子當(dāng)你的一個(gè)單頁面應(yīng)用很大的時(shí)候,你可能就需要將每個(gè)路由拆分到一個(gè) chunk 中,這樣才方便我們實(shí)現(xiàn)按需加載。

代碼分離是 webpack 中最引人注目的特性之一。此特性能夠把代碼分離到不同的 bundle 中,然后可以按需加載或并行加載這些文件。代碼分離可以用于獲取更小的 bundle,以及控制資源加載優(yōu)先級(jí),如果使用合理,會(huì)極大影響加載時(shí)間。

二、關(guān)于代碼分割

接下來我們會(huì)分別分析不同的代碼分隔方式帶來的打包差異,首先我們的項(xiàng)目假設(shè)有這兩個(gè)簡單的文件??

index.js

  1. import { mul } from './test' 
  2. import $ from 'jquery' 
  3.  
  4. console.log($) 
  5. console.log(mul(2, 3)) 

test.js

  1. import $ from 'jquery' 
  2.  
  3. console.log($) 
  4.  
  5. function mul(a, b) { 
  6.     return a * b 
  7.  
  8. export { mul } 

可以看到現(xiàn)在他們二者都依賴于 jquery 這個(gè)庫,并且相互之間也會(huì)有依賴。

當(dāng)我們?cè)谀J(rèn)配置的情況下進(jìn)行打包,結(jié)果是這樣的??,會(huì)把所有內(nèi)容打包進(jìn)一個(gè) main bundle 內(nèi)(324kb)

那么我們?nèi)绾斡米钪苯拥姆绞綇倪@個(gè) bundle 中分離出其他模塊呢?

1. 多入口

webpack 配置中的 entry ,可以設(shè)置為多個(gè),也就是說我們可以分別將 index 和 test 文件分別作為入口:

  1. // entry: './src/index.js', 原來的單入口 
  2. /** 現(xiàn)在分別將它們作為入口 */ 
  3. entry:{ 
  4.   index:'./src/index.js'
  5.   test:'./src/test.js' 
  6. }, 
  7. output: { 
  8.   filename: '[name].[hash:8].js'
  9.   path: path.resolve(__dirname, './dist'), 
  10. }, 

這樣讓我們看一下這樣打包后的結(jié)果:

確實(shí)打包出了兩個(gè)文件!但是為什么兩個(gè)文件都有 320+kb 呢?不是說好拆分獲取更小的 bundle ?這是因?yàn)橛捎诙叨家肓?jquery 而 webpack 從兩次入口進(jìn)行打包分析的時(shí)候會(huì)每次都將依賴的模塊分別打包進(jìn)去

沒錯(cuò),這種配置的方式確實(shí)會(huì)帶來一些隱患以及不便:

  • 如果入口 chunk 之間包含一些重復(fù)的模塊,那些重復(fù)模塊都會(huì)被引入到各個(gè) bundle 中。
  • 這種方法不夠靈活,并且不能動(dòng)態(tài)地將核心應(yīng)用程序邏輯中的代碼拆分出來。

那么有沒有方式可以既可以將共同依賴的模塊進(jìn)行打包分離,又不用進(jìn)行繁瑣的手動(dòng)配置入口的方式呢?那必然是有的。

2. SplitChunksPlugin

SplitChunks 是 webpack5 自帶的開箱即用的一個(gè)插件,他可以將滿足規(guī)則的 chunk 進(jìn)行分離,也可以自定義配置。在 webpack5 中用它取代了 webpack4 中的用來解決重復(fù)依賴的 CommonsChunkPlugin 。

讓我們?cè)谖覀兊?webpack 配置中加上一些配置:

  1. entry: './src/index.js', // 這里我們改回單入口 
  2. /** 加上如下設(shè)置 */ 
  3. optimization: { 
  4.   splitChunks: { 
  5.     chunks: 'all'
  6.   }, 
  7. }, 

打包后的結(jié)果如圖:

可以看到很明顯除了根據(jù)入口打包出的 main bundle 之外,還多出了一個(gè)名為 vendors-node_modules_jquery_dist_jquery_js.xxxxx.js ,顯然這樣我們將公用的 jquery 模塊就提取出來了。

接下來我們來探究一下 SplitChunksPlugin 。首先看下配置的默認(rèn)值:

  1. splitChunks: { 
  2.     // 表示選擇哪些 chunks 進(jìn)行分割,可選值有:async,initial 和 all 
  3.     chunks: "async"
  4.     // 表示新分離出的 chunk 必須大于等于 minSize,20000,約 20kb。 
  5.     minSize: 20000, 
  6.     // 通過確保拆分后剩余的最小 chunk 體積超過限制來避免大小為零的模塊,僅在剩余單個(gè) chunk 時(shí)生效 
  7.     minRemainingSize: 0, 
  8.     // 表示一個(gè)模塊至少應(yīng)被 minChunks 個(gè) chunk 所包含才能分割。默認(rèn)為 1。 
  9.     minChunks: 1, 
  10.     // 表示按需加載文件時(shí),并行請(qǐng)求的最大數(shù)目。 
  11.     maxAsyncRequests: 30, 
  12.     // 表示加載入口文件時(shí),并行請(qǐng)求的最大數(shù)目。 
  13.     maxInitialRequests: 30, 
  14.     // 強(qiáng)制執(zhí)行拆分的體積閾值和其他限制(minRemainingSize,maxAsyncRequests,maxInitialRequests)將被忽略 
  15.     enforceSizeThreshold: 50000, 
  16.     // cacheGroups 下可以可以配置多個(gè)組,每個(gè)組根據(jù) test 設(shè)置條件,符合 test 條件的模塊,就分配到該組。模塊可以被多個(gè)組引用,但最終會(huì)根據(jù) priority 來決定打包到哪個(gè)組中。默認(rèn)將所有來自 node_modules 目錄的模塊打包至 vendors 組,將兩個(gè)以上的 chunk 所共享的模塊打包至 default 組。 
  17.     cacheGroups: { 
  18.         defaultVendors: { 
  19.             test: /[\\/]node_modules[\\/]/, 
  20.             // 一個(gè)模塊可以屬于多個(gè)緩存組。優(yōu)化將優(yōu)先考慮具有更高 priority(優(yōu)先級(jí))的緩存組。 
  21.             priority: -10, 
  22.             // 如果當(dāng)前 chunk 包含已從主 bundle 中拆分出的模塊,則它將被重用 
  23.             reuseExistingChunk: true
  24.         }, 
  25.        default: { 
  26.             minChunks: 2, 
  27.             priority: -20, 
  28.             reuseExistingChunk: true 
  29.         } 
  30.     } 

默認(rèn)情況下,SplitChunks 只會(huì)對(duì)異步調(diào)用的模塊進(jìn)行分割(chunks: "async"),并且默認(rèn)情況下處理的 chunk 至少要有 20kb ,過小的模塊不會(huì)被包含進(jìn)去。

補(bǔ)充一下,默認(rèn)值會(huì)根據(jù) mode 的配置不同有所變化,具體參見源碼:

  1. const { splitChunks } = optimization; 
  2. if (splitChunks) { 
  3.   A(splitChunks, "defaultSizeTypes", () => ["javascript""unknown"]); 
  4.   D(splitChunks, "hidePathInfo", production); 
  5.   D(splitChunks, "chunks""async"); 
  6.   D(splitChunks, "usedExports", optimization.usedExports === true); 
  7.   D(splitChunks, "minChunks", 1); 
  8.   F(splitChunks, "minSize", () => (production ? 20000 : 10000)); 
  9.   F(splitChunks, "minRemainingSize", () => (development ? 0 : undefined)); 
  10.   F(splitChunks, "enforceSizeThreshold", () => (production ? 50000 : 30000)); 
  11.   F(splitChunks, "maxAsyncRequests", () => (production ? 30 : Infinity)); 
  12.   F(splitChunks, "maxInitialRequests", () => (production ? 30 : Infinity)); 
  13.   D(splitChunks, "automaticNameDelimiter""-"); 
  14.   const { cacheGroups } = splitChunks; 
  15.   F(cacheGroups, "default", () => ({ 
  16.     idHint: ""
  17.     reuseExistingChunk: true
  18.     minChunks: 2, 
  19.     priority: -20 
  20.   })); 
  21.   F(cacheGroups, "defaultVendors", () => ({ 
  22.     idHint: "vendors"
  23.     reuseExistingChunk: true
  24.     test: NODE_MODULES_REGEXP, 
  25.     priority: -10 
  26.   })); 

cacheGroups 緩存組是施行分割的重中之重,他可以使用來自 splitChunks.* 的任何選項(xiàng),但是 test、priority 和 reuseExistingChunk 只能在緩存組級(jí)別上進(jìn)行配置。默認(rèn)配置中已經(jīng)給我們提供了 Vendors 組和一個(gè) defalut 組,**Vendors **組中使用 test: /[\\/]node_modules[\\/]/ 匹配了 node_modules 中的所有符合規(guī)則的模塊。

Tip:當(dāng) webpack 處理文件路徑時(shí),它們始終包含 Unix 系統(tǒng)中的 / 和 Windows 系統(tǒng)中的 \。這就是為什么在 {cacheGroup}.test 字段中使用 [\/] 來表示路徑分隔符的原因。{cacheGroup}.test 中的 / 或 \ 會(huì)在跨平臺(tái)使用時(shí)產(chǎn)生問題。

綜上的配置,我們便可以理解為什么我們?cè)诖虬袝?huì)產(chǎn)生出名為 vendors-node_modules_jquery_dist_jquery_js.db47cc72.js 的文件了。如果你想要對(duì)名稱進(jìn)行自定義的話,也可以使用 splitChunks.name 屬性(每個(gè) cacheGroup 中都可以使用),這個(gè)屬性支持使用三種形式:

  • boolean = false 設(shè)為 false 將保持 chunk 的相同名稱,因此不會(huì)不必要地更改名稱。這是生產(chǎn)環(huán)境下構(gòu)建的建議值。
  • function (module, chunks, cacheGroupKey) => string 返回值要求是 string 類型,并且在 chunks 數(shù)組中每一個(gè) chunk 都有 chunk.name 和 chunk.hash 屬性,舉個(gè)例子 
  1. name(module, chunks, cacheGroupKey) { 
  2.   const moduleFileName = module 
  3.   .identifier() 
  4.   .split('/'
  5.   .reduceRight((item) => item); 
  6.   const allChunksNames = chunks.map((item) => item.name).join('~'); 
  7.   return `${cacheGroupKey}-${allChunksNames}-${moduleFileName}`; 
  8. }, 
  • string 指定字符串或始終返回相同字符串的函數(shù)會(huì)將所有常見模塊和 vendor 合并為一個(gè) chunk。這可能會(huì)導(dǎo)致更大的初始下載量并減慢頁面加載速度。

另外注意一下 splitChunks.maxAsyncRequests 和 splitChunks.maxInitialRequests 分別指的是按需加載時(shí)最大的并行請(qǐng)求數(shù)和頁面初始渲染時(shí)候需要的最大并行請(qǐng)求數(shù)

在我們的項(xiàng)目較大時(shí),如果需要對(duì)某個(gè)依賴單獨(dú)拆包的話,可以進(jìn)行這樣的配置:

  1. cacheGroups: { 
  2.   react: { 
  3.     name'react'
  4.       test: /[\\/]node_modules[\\/](react)/, 
  5.       chunks: 'all'
  6.       priority: -5, 
  7.   }, 
  8.  }, 

這樣打包后就可以拆分指定的包:

更多配置詳見官網(wǎng)配置文檔

3. 動(dòng)態(tài) import

使用 import()語法 來實(shí)現(xiàn)動(dòng)態(tài)導(dǎo)入也是我們非常推薦的一種代碼分割的方式,我們先來簡單修改一下我們的 index.js ,再來看一下使用后打包的效果:

  1. // import { mul } from './test' 
  2. import $ from 'jquery' 
  3.  
  4. import('./test').then(({ mul }) => { 
  5.     console.log(mul(2,3)) 
  6. }) 
  7.  
  8. console.log($) 
  9. // console.log(mul(2, 3)) 

可以看到,通過 import() 語法導(dǎo)入的模塊在打包時(shí)會(huì)自動(dòng)單獨(dú)進(jìn)行打包

值得注意的是,這種語法還有一種很方便的“動(dòng)態(tài)引用”的方式,他可以加入一些適當(dāng)?shù)谋磉_(dá)式,舉個(gè)例子,假設(shè)我們需要加載適當(dāng)?shù)闹黝}:

  1. const themeType = getUserTheme(); 
  2. import(`./themes/${themeType}`).then((module) => { 
  3.   // do sth aboout theme 
  4. }); 

這樣我們就可以“動(dòng)態(tài)”加載我們需要的異步模塊,實(shí)現(xiàn)的原理主要在于兩點(diǎn):

至少需要包含模塊相關(guān)的路徑信息,打包可以限定于一個(gè)特定的目錄或文件集。

根據(jù)路徑信息 webpack 在打包時(shí)會(huì)把 ./themes 中的所有文件打包進(jìn)新的 chunk 中,以便需要時(shí)使用到。

4. 魔術(shù)注釋

在上述的 import() 語法中,我們會(huì)發(fā)現(xiàn)打包自動(dòng)生成的文件名并不是我們想要的,我們?nèi)绾尾拍茏约嚎刂拼虬拿Q呢?這里就要引入我們的魔術(shù)注釋(Magic Comments):

  1. import(/* webpackChunkName: "my-chunk-name" */'./test'

通過這樣打包出來的文件:

魔術(shù)注釋不僅僅可以幫我們修改 chunk 名這么簡單,他還可以實(shí)現(xiàn)譬如預(yù)加載等功能,這里舉個(gè)例子:

我們通過希望在點(diǎn)擊按鈕時(shí)才加載我們需要的模塊功能,代碼可以這樣:

  1. // index.js 
  2. document.querySelector('#btn').onclick = function () { 
  3.   import('./test').then(({ mul }) => { 
  4.     console.log(mul(2, 3)); 
  5.   }); 
  6. }; 
  7. //test.js 
  8. function mul(a, b) { 
  9.   return a * b; 
  10. console.log('test 被加載了'); 
  11. export { mul }; 

可以看到,在我們點(diǎn)擊按鈕的同時(shí)確實(shí)加載了 test.js 的文件資源。但是如果這個(gè)模塊是一個(gè)很大的模塊,在點(diǎn)擊時(shí)進(jìn)行加載可能會(huì)造成長時(shí)間 loading 等用戶體驗(yàn)不是很好的效果,這個(gè)時(shí)候我們可以使用我們的 /* webpackPrefetch: true */ 方式進(jìn)行預(yù)獲取,來看下效果:

  1. // index,js 
  2.  
  3. document.querySelector('#btn').onclick = function () { 
  4.   import(/* webpackPrefetch: true */'./test').then(({ mul }) => { 
  5.     console.log(mul(2, 3)); 
  6.   }); 
  7. }; 

可以看到整個(gè)過程中,在畫面初始加載的時(shí)候,test.js 的資源就已經(jīng)被預(yù)先加載了,而在我們點(diǎn)擊按鈕時(shí),會(huì)從 (prefetch cache) 中讀取內(nèi)容。這就是模塊預(yù)獲取的過程。另外我們還有 /* webpackPreload: true */ 的方式進(jìn)行預(yù)加載。

但是 prefetch 和 preload 聽起來感覺差不多,實(shí)際上他們的加載時(shí)機(jī)等是完全不同的:

  • preload chunk 會(huì)在父 chunk 加載時(shí),以并行方式開始加載。prefetch chunk 會(huì)在父 chunk 加載結(jié)束后開始加載。
  • preload chunk 具有中等優(yōu)先級(jí),并立即下載。prefetch chunk 在瀏覽器閑置時(shí)下載。
  • preload chunk 會(huì)在父 chunk 中立即請(qǐng)求,用于當(dāng)下時(shí)刻。prefetch chunk 會(huì)用于未來的某個(gè)時(shí)刻。

三、結(jié)尾

在最初有工程化打包思想時(shí),我們會(huì)考慮將多文件打包到一個(gè)文件內(nèi)減少多次的資源請(qǐng)求,隨著項(xiàng)目的越來越復(fù)雜,做項(xiàng)目優(yōu)化時(shí),我們發(fā)現(xiàn)項(xiàng)目加載越久用戶體驗(yàn)就越不好,于是又可以通過代碼分割的方式去減少頁面初加載時(shí)的請(qǐng)求過大的資源體積。

本文中僅簡單介紹了常用的 webpack 代碼分割方式,但是在實(shí)際的項(xiàng)目中進(jìn)行性能優(yōu)化時(shí),往往會(huì)有更加嚴(yán)苛的要求,希望可以通過本文的介紹讓大家快速了解上手代碼分割的技巧與優(yōu)勢。

參考

如何使用 splitChunks 精細(xì)控制代碼分割

Code Splitting - Webpack

 

責(zé)任編輯:武曉燕 來源: 微醫(yī)大前端技術(shù)
相關(guān)推薦

2016-11-28 16:23:23

戴爾

2022-05-09 08:35:43

面試產(chǎn)品互聯(lián)網(wǎng)

2022-09-02 09:09:25

項(xiàng)目ReactES6

2021-03-10 09:21:00

Spring開源框架Spring基礎(chǔ)知識(shí)

2023-03-06 11:35:55

經(jīng)營分析體系

2017-03-19 15:47:50

神經(jīng)網(wǎng)絡(luò)

2024-12-02 11:24:30

Docker編排技術(shù)

2022-03-15 11:51:00

決策分析模型

2022-11-03 11:31:43

結(jié)構(gòu)分析法監(jiān)測

2023-12-07 07:14:36

WebpackVite

2014-03-17 11:05:00

ScriptCode Blocks

2019-07-31 10:18:17

Web 開發(fā)Python

2023-03-19 17:36:38

2022-04-07 10:02:58

前端檢測工具

2017-05-27 09:23:10

IOS框架APP框架代碼

2022-06-13 07:02:02

Zadig平臺(tái)自動(dòng)化

2023-03-22 11:41:56

2018-01-16 12:31:33

Python爬蟲數(shù)據(jù)

2019-10-22 08:12:49

消息隊(duì)列分布式系統(tǒng)

2015-06-12 10:03:05

QQ瀏覽器
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)

主站蜘蛛池模板: 在线日韩精品视频 | 自拍偷拍欧美 | 亚洲网站在线观看 | av网站观看 | 91久久精品日日躁夜夜躁国产 | 日韩在线欧美 | 特级黄一级播放 | 麻豆久久 | 欧美国产日韩在线观看成人 | 亚洲一区二区在线 | 中文字幕免费观看 | 国产伦一区二区三区久久 | 精品粉嫩超白一线天av | 波波电影院一区二区三区 | 欧美伊人影院 | 一区二区三区国产在线观看 | 日韩免费一区 | 找个黄色片 | v亚洲 | avhd101在线成人播放 | 日韩欧美专区 | 亚洲三区在线观看 | 蜜桃臀av一区二区三区 | 欧美日韩精品一区二区天天拍 | 亚洲欧美激情四射 | 国产高清视频一区 | 啪啪免费 | 国产一区二区三区视频 | 久久99成人 | 欧美日本一区 | 国产玖玖 | 精品视频成人 | 欧美1区| 亚洲日日夜夜 | 午夜影视在线观看 | 国产区精品 | 国产精品久久久久免费 | 亚洲一区二区三区免费视频 | 三级黄视频在线观看 | 欧美激情久久久 | 欧美激情久久久 |