一文搞懂webpack?hash持久化的原理
理解 module、chunk 和 bundle
module 就是我們通過 import 引入的各種模塊
chunk 是 webpack 根據(jù)功能拆分出來的模塊,包括入口文件, 動(dòng)態(tài) import,lazy 等的文件以及 splitChunks 拆分出來的代碼,chunk 可能包含多個(gè) module
bundle 就是 webpack 打包之后的各個(gè)文件,于 chunk 一般一一對(duì)應(yīng)
hash 的分類
hash:the hash of the module identifier(根據(jù) module_id 序列的變化而變化)
chunkHash:the hash of the chunk content(chunkHash,根據(jù)每一個(gè) chunk 內(nèi)容的變化而變化)
contentHash:the hash of extracted content(根據(jù)內(nèi)容變化而變化)
hash
compilation
- webpack 的 hash 是根據(jù) compilation 計(jì)算出來的,compilation 對(duì)象代表某個(gè)版本的資源對(duì)應(yīng)的編譯進(jìn)程,當(dāng)我們的文件發(fā)生改變的時(shí)候, 進(jìn)而能夠針對(duì)改動(dòng)生產(chǎn)全新的編譯文件。compilation 對(duì)象包含當(dāng)前模塊資源、待編譯文件、有改動(dòng)的文件和監(jiān)聽依賴的所有信息,如果我們修改某一個(gè)文件,那么此時(shí)整個(gè)項(xiàng)目的 hash 都會(huì)改變
compiler
- compiler 對(duì)象代表的是配置完備的 Webpack 環(huán)境。 compiler 對(duì)象只在 Webpack 啟動(dòng)時(shí)構(gòu)建一次,由 Webpack 組合所有的配置項(xiàng)構(gòu)建生成,compiler 對(duì)象代表的是不變的 webpack 環(huán)境,compilation 是針對(duì)隨時(shí)可變的項(xiàng)目文件
module_id
- webpack 通過給每一個(gè)模塊一個(gè) module_id 來處理各個(gè)模塊之間的依賴關(guān)系,而默認(rèn)的 id 命名規(guī)則是根據(jù)模塊引入的順序賦予一個(gè)整數(shù)(1,2,3),所以任意的增添或者刪除一個(gè)模塊的依賴,都會(huì)對(duì)整個(gè)的 ID 序列產(chǎn)生影響,最后影響 hash 值,這些模塊會(huì)被 runtime 和 manifest 和引用到
對(duì)于圖片、字體、PDF 等資源該 hash 還是可以生成一個(gè)唯一值的
此時(shí)我們配置 webpack 的 output 為 hash
// 此時(shí)項(xiàng)目的 mode: 'production', entry: { app: [path.resolve(__dirname, '../src/index.js')], }, output: { filename: 'js/[name].[hash].js', hashDigestLength: 7, path: path.resolve(__dirname, '../dist'), publicPath: './', },
項(xiàng)目依賴打包情況如下,我們可以看到所有的 hash 的值都是一樣的
Asset Size Chunks Chunk Names css/app.2f3933e.css 52 bytes 0 [emitted] [immutable] app css/list.2f3933e.css 1.5 KiB 1 [emitted] [immutable] list css/vendors.2f3933e.css 71.2 KiB 2 [emitted] [immutable] vendors css/vendors.2f3933e.css.gz 7.85 KiB [emitted] index.html 1.33 KiB [emitted] js/app.2f3933e.js 6.63 KiB 0 [emitted] [immutable] app js/list.2f3933e.js 50.9 KiB 1 [emitted] [immutable] list js/list.2f3933e.js.LICENSE 120 bytes [emitted] js/list.2f3933e.js.gz 15 KiB [emitted] js/vendors.2f3933e.js 340 KiB 2 [emitted] [immutable] [big] vendors js/vendors.2f3933e.js.LICENSE 423 bytes [emitted] js/vendors.2f3933e.js.gz 91.8 KiB [emitted] js/work.2f3933e.js 188 bytes 3 [emitted] [immutable] work
runtime 和 manifest
webpack 通過 runtime 和 manifest 來管理所有模塊的交互
runtime
- runtime,以及伴隨的 manifest 數(shù)據(jù),主要是指:在瀏覽器運(yùn)行過程中,webpack 用來連接模塊化應(yīng)用程序所需的所有代碼。它包含:在模塊交互時(shí),連接模塊所需的加載和解析邏輯。包括:已經(jīng)加載到瀏覽器中的連接模塊邏輯,以及尚未加載模塊的延遲加載邏輯
manifest
- 當(dāng) compiler 開始執(zhí)行、解析和映射應(yīng)用程序時(shí),它會(huì)保留所有模塊的詳細(xì)要點(diǎn)。這個(gè)數(shù)據(jù)集合稱為 "manifest",當(dāng)完成打包并發(fā)送到瀏覽器時(shí),runtime 會(huì)通過 manifest 來解析和加載模塊。無論你選擇哪種 模塊語法,那些 import 或 require 語句現(xiàn)在都已經(jīng)轉(zhuǎn)換為 webpack_require 方法,此方法指向模塊標(biāo)識(shí)符(module identifier)。通過使用 manifest 中的數(shù)據(jù),runtime 將能夠檢索這些標(biāo)識(shí)符,找出每個(gè)標(biāo)識(shí)符背后對(duì)應(yīng)的模塊
runtime 和 manifest 是一個(gè)每次打包都可能變化的不穩(wěn)定的因素,所以他會(huì)導(dǎo)致一些問題,比如,我們對(duì)整個(gè)項(xiàng)目的文章在做一次打包,打包結(jié)果如下,我們發(fā)現(xiàn),我們什么也沒有改動(dòng)但是 hash 全部發(fā)生了變化,原因就是 runtime 和 manifest 這些所謂的樣板文件
Asset Size Chunks Chunk Names css/app.2f3933e.css 52 bytes 0 [emitted] [immutable] app css/list.2f3933e.css 1.5 KiB 1 [emitted] [immutable] list css/vendors.2f3933e.css 71.2 KiB 2 [emitted] [immutable] vendors css/vendors.2f3933e.css.gz 7.85 KiB [emitted] index.html 1.33 KiB [emitted] js/app.2f3933e.js 6.63 KiB 0 [emitted] [immutable] app js/list.2f3933e.js 50.9 KiB 1 [emitted] [immutable] list js/list.2f3933e.js.LICENSE 120 bytes [emitted] js/list.2f3933e.js.gz 15 KiB [emitted] js/vendors.2f3933e.js 340 KiB 2 [emitted] [immutable] [big] vendors js/vendors.2f3933e.js.LICENSE 423 bytes [emitted] js/vendors.2f3933e.js.gz 91.8 KiB [emitted] js/work.2f3933e.js 188 bytes 3 [emitted] [immutable] work
如何解決這個(gè)問題
- 我們可以把 runtime 和 manifest 提取出來,去掉這兩個(gè)不穩(wěn)定因素,然后打包發(fā)現(xiàn) hash 并未改變,但是我們多了一個(gè) mainfest 文件
optimization: { runtimeChunk: { name: 'manifest', }, }
再次打包代碼,不斷的打包 hash 都不會(huì)改變
Asset Size Chunks Chunk Names css/app.c870f3f.css 52 bytes 0 [emitted] [immutable] app css/list.c870f3f.css 1.5 KiB 1 [emitted] [immutable] list css/vendors.c870f3f.css 71.2 KiB 3 [emitted] [immutable] vendors css/vendors.c870f3f.css.gz 7.85 KiB [emitted] index.html 1.4 KiB [emitted] js/app.c870f3f.js 3.62 KiB 0 [emitted][immutable] app js/list.c870f3f.js 50.9 KiB 1 [emitted][immutable] list js/list.c870f3f.js.LICENSE 120 bytes [emitted] js/list.c870f3f.js.gz 15 KiB [emitted] js/manifest.c870f3f.js 3.07 KiB 2 [emitted][immutable] manifest js/vendors.c870f3f.js 340 KiB 3 [emitted][immutable] [big] vendors js/vendors.c870f3f.js.LICENSE 423 bytes [emitted] js/vendors.c870f3f.js.gz 91.8 KiB [emitted] js/work.c870f3f.js 188 bytes 4 [emitted][immutable] work
chunkhash
chunk 就是模塊。chunkhash 也就是根據(jù)模塊內(nèi)容計(jì)算出的 hash 值,很顯然,hash 并不適合做本地持久化,所以我們使用 chunkhash 此時(shí)修改 webpack 的配置
```javascript optimization: webpackBase.optimization, mode: 'production', entry: { app: [path.resolve(__dirname, '../src/index.js')], } ```
修改配置之后打包的結(jié)果是(CSS 的結(jié)果還是一樣的,我們稍后處理)
Asset Size Chunks Chunk Names css/app.8b9de76.css 71.3 KiB 0 [emitted] [immutable] app css/app.8b9de76.css.gz 7.88 KiB [emitted] css/vendors.8b9de76.css 1.5 KiB 3 [emitted] [immutable] vendors index.html 1.27 KiB [emitted] js/app.0df5dd7.js 340 KiB 0 [emitted] [immutable] [big] app js/app.0df5dd7.js.LICENSE 423 bytes [emitted] js/app.0df5dd7.js.gz 92.5 KiB [emitted] js/list.111956e.js 2.48 KiB 1 [emitted] [immutable] list js/manifest.aa8eb6d.js 3.13 KiB 2 [emitted] [immutable] manifest js/vendors.49e3e7f.js 48.5 KiB 3 [emitted] [immutable] vendors js/vendors.49e3e7f.js.LICENSE 120 bytes [emitted] js/vendors.49e3e7f.js.gz 13.8 KiB [emitted] js/work.1b2fd82.js 188 bytes 4 [emitted] [immutable] work
這個(gè)時(shí)候修改 list.js,然后繼續(xù)打包,css 的 hash 變了,正常因?yàn)樗褂玫氖?hash 不是 chunkhash,list 的 hash 也變了,正常因?yàn)槲覀冃薷牧诉@個(gè)文件,work 的 hash 并沒有變化,完全正常
```javascript Asset Size Chunks Chunk Names css/app.1a93a35.css 71.3 KiB 0 [emitted] [immutable] app css/app.1a93a35.css.gz 7.88 KiB [emitted] css/vendors.1a93a35.css 1.5 KiB 3 [emitted] [immutable] vendors index.html 1.27 KiB [emitted] js/app.0df5dd7.js 340 KiB 0 [emitted] [immutable] [big] app js/app.0df5dd7.js.LICENSE 423 bytes [emitted] js/app.0df5dd7.js.gz 92.5 KiB [emitted] js/list.5b187e3.js 2.48 KiB 1 [emitted] [immutable] list js/manifest.3752b77.js 3.13 KiB 2 [emitted] [immutable] manifest js/vendors.49e3e7f.js 48.5 KiB 3 [emitted] [immutable] vendors js/vendors.49e3e7f.js.LICENSE 120 bytes [emitted] js/vendors.49e3e7f.js.gz 13.8 KiB [emitted] js/work.1b2fd82.js 188 bytes 4 [emitted] [immutable] work ```
這個(gè)時(shí)候我們?yōu)?list.js 引入一個(gè)新的 js,css 改變我們暫且不論,這個(gè)時(shí)候發(fā)現(xiàn) vendors.js, app.js, work.js 竟然全部改變了, 這不符合我們的預(yù)期,這是因?yàn)槊總€(gè) module.id 會(huì)基于默認(rèn)的解析順序(resolve order)進(jìn)行增量(類似于沒有指定 key 的 react 的組件的渲染)。也就是說,當(dāng)解析順序發(fā)生變化,ID 也會(huì)隨之改變,所以我們需要自己命名這個(gè) moduleid
```javascript Asset Size Chunks Chunk Names css/app.636f1cd.css 52 bytes 0 [emitted] [immutable] app css/list.636f1cd.css 1.5 KiB 1 [emitted] [immutable] list css/vendors.636f1cd.css 71.2 KiB 3 [emitted] [immutable] vendors css/vendors.636f1cd.css.gz 7.85 KiB [emitted] index.html 1.4 KiB [emitted] js/app.0b4c163.js 3.62 KiB 0 [emitted][immutable] app js/list.d02fa6a.js 51 KiB 1 [emitted][immutable] list js/list.d02fa6a.js.LICENSE 120 bytes [emitted] js/list.d02fa6a.js.gz 15 KiB [emitted] js/manifest.3a9ff17.js 3.09 KiB 2 [emitted][immutable] manifest js/vendors.a1bfd17.js 340 KiB 3 [emitted][immutable] [big] vendors js/vendors.a1bfd17.js.LICENSE 423 bytes [emitted] js/vendors.a1bfd17.js.gz 91.8 KiB [emitted] js/work.f70d2d8.js 188 bytes 4 [emitted][immutable] work ```
我們自己命名這個(gè) ID 把,命名的方式如下
// 將默認(rèn)的數(shù)字 id 命名規(guī)則換成路徑的方式。webpack 4 中當(dāng) mode 為 development 會(huì)默認(rèn)啟動(dòng) optimization: { namedModules: true } // 但是如果把路徑作為ID難免太長,所以我們使用HashedModuleIdsPlugin來生成hash plugins: [ new webpack.HashedModuleIdsPlugin(), ], // 此時(shí)進(jìn)行打包的結(jié)果是 Asset Size Chunks Chunk Names css/app.d36a7df.css 52 bytes 0 [emitted] [immutable] app css/list.d36a7df.css 1.5 KiB 1 [emitted] [immutable] list css/vendors.d36a7df.css 71.2 KiB 3 [emitted] [immutable] vendors css/vendors.d36a7df.css.gz 7.85 KiB [emitted] index.html 1.4 KiB [emitted] js/app.5aef12b.js 3.74 KiB 0 [emitted] [immutable] app js/list.9dbf1d7.js 51.5 KiB 1 [emitted] [immutable] list js/list.9dbf1d7.js.LICENSE 120 bytes [emitted] js/list.9dbf1d7.js.gz 15.7 KiB [emitted] js/manifest.cf2b1ee.js 3.09 KiB 2 [emitted] [immutable] manifest js/vendors.612571f.js 343 KiB 3 [emitted] [immutable] [big] vendors js/vendors.612571f.js.LICENSE 423 bytes [emitted] js/vendors.612571f.js.gz 97.7 KiB [emitted] js/work.3d8d43d.js 196 bytes 4 [emitted] [immutable] work
此時(shí)為 list 再次 import 一個(gè)文件,打包之后 hash 的值是,此時(shí)我們發(fā)現(xiàn) app.js 的值沒有變,list 的值改變了,vendors 和 work 都沒變完全符合我們的預(yù)期,至此 js hash 的過程已經(jīng)完全結(jié)束
```javascript Asset Size Chunks Chunk Names css/app.39db041.css 52 bytes 0 [emitted] [immutable] app css/list.39db041.css 1.5 KiB 1 [emitted] [immutable] list css/vendors.39db041.css 71.2 KiB 3 [emitted] [immutable] vendors css/vendors.39db041.css.gz 7.85 KiB [emitted] index.html 1.4 KiB [emitted] js/app.5aef12b.js 3.74 KiB 0 [emitted] [immutable] app js/list.a0c9911.js 51.5 KiB 1 [emitted] [immutable] list js/list.a0c9911.js.LICENSE 120 bytes [emitted] js/list.a0c9911.js.gz 15.7 KiB [emitted] js/manifest.14406a1.js 3.09 KiB 2 [emitted] [immutable] manifest js/vendors.612571f.js 343 KiB 3 [emitted] [immutable] [big] vendors js/vendors.612571f.js.LICENSE 423 bytes [emitted] js/vendors.612571f.js.gz 97.7 KiB [emitted] js/work.3d8d43d.js 196 bytes 4 [emitted] [immutable] work ```
contentHash
之前我們還有一個(gè)遺留問題,就是 css 的 hash 每次都會(huì)產(chǎn)生變化,是因?yàn)槲覀冎芭渲昧顺殡x的 css 是 hash,根據(jù)上面的文章,我們修改為 chunkhash
```javascript // 之前的配置 miniCssExtract: new MiniCssExtractPlugin({ filename: 'css/[name].[hash].css', chunkFilename: 'css/[name].[hash].css', ignoreOrder: false, }); // 修改之后的配置 miniCssExtract: new MiniCssExtractPlugin({ filename: 'css/[name].[chunkhash].css', chunkFilename: 'css/[name].[chunkhash].css', ignoreOrder: false, }); ```
修改為 chunkhash 之后,當(dāng)然 css 的值就不會(huì)每次都發(fā)生變化了,此時(shí)我們對(duì)項(xiàng)目進(jìn)行打包,然后修改 work.js 我們會(huì)發(fā)現(xiàn) css 的 hash 并沒有發(fā)生(此處不在嘗試) 任何變化,完全符合我們的預(yù)期,但是我們卻發(fā)現(xiàn),我們是以 chunk 做 hash,所以導(dǎo)致了一個(gè)問題,list.js 和 list.css 的 hash 值一摸一樣,因?yàn)樗麄儗儆谕粋€(gè) chunk
```javascript Asset Size Chunks Chunk Names css/app.5aef12b.css 52 bytes 0 [emitted] [immutable] app css/list.a0c9911.css 1.5 KiB 1 [emitted] [immutable] list css/vendors.612571f.css 71.2 KiB 3 [emitted] [immutable] vendors css/vendors.612571f.css.gz 7.85 KiB [emitted] index.html 1.4 KiB [emitted] js/app.5aef12b.js 3.74 KiB 0 [emitted] [immutable] app js/list.a0c9911.js 51.5 KiB 1 [emitted] [immutable] list js/list.a0c9911.js.LICENSE 120 bytes [emitted] js/list.a0c9911.js.gz 15.7 KiB [emitted] js/manifest.171619f.js 3.12 KiB 2 [emitted] [immutable] manifest js/vendors.612571f.js 343 KiB 3 [emitted] [immutable] [big] vendors js/vendors.612571f.js.LICENSE 423 bytes [emitted] js/vendors.612571f.js.gz 97.7 KiB [emitted] js/work.3d8d43d.js 196 bytes 4 [emitted] [immutable] work ```
對(duì) app.css 做修改,然后重新打包,打包結(jié)果如下,我們發(fā)現(xiàn),app.css 的 hash 發(fā)生了變化,但是 app.js 的 hash 也發(fā)生了變化,這就是因?yàn)?app.css 和 app.js 屬于同一個(gè) chunk,所以這個(gè)時(shí)候我們就必須對(duì) css 單獨(dú)處理讓他根據(jù)自己的 content 去做 hash 而不是 chunk
```javascript Asset Size Chunks Chunk Names css/app.131454e.css 52 bytes 0 [emitted] [immutable] app css/list.a0c9911.css 1.5 KiB 1 [emitted] [immutable] list css/vendors.612571f.css 71.2 KiB 3 [emitted] [immutable] vendors css/vendors.612571f.css.gz 7.85 KiB [emitted] index.html 1.4 KiB [emitted] js/app.131454e.js 3.74 KiB 0 [emitted] [immutable] app js/list.a0c9911.js 51.5 KiB 1 [emitted] [immutable] list js/list.a0c9911.js.LICENSE 120 bytes [emitted] js/list.a0c9911.js.gz 15.7 KiB [emitted] js/manifest.171619f.js 3.12 KiB 2 [emitted] [immutable] manifest js/vendors.612571f.js 343 KiB 3 [emitted] [immutable] [big] vendors js/vendors.612571f.js.LICENSE 423 bytes [emitted] js/vendors.612571f.js.gz 97.7 KiB [emitted] js/work.3d8d43d.js 196 bytes 4 [emitted] [immutable] work ```
修改配置然后重新打包代碼
// 修改配置 miniCssExtract: new MiniCssExtractPlugin({ filename: 'css/[name].[contenthash].css', chunkFilename: 'css/[name].[contenthash].css', ignoreOrder: false, }); /* 重新打包代碼如下,可以看到app.js和app.css的hash不一致了 */ Asset Size Chunks Chunk Names css/app.15e0de3.css 52 bytes 0 [emitted] [immutable] app css/list.8298c67.css 1.5 KiB 1 [emitted] [immutable] list css/vendors.353f491.css 71.2 KiB 3 [emitted] [immutable] vendors css/vendors.353f491.css.gz 7.85 KiB [emitted] index.html 1.4 KiB [emitted] js/app.131454e.js 3.74 KiB 0 [emitted] [immutable] app js/list.a0c9911.js 51.5 KiB 1 [emitted] [immutable] list js/list.a0c9911.js.LICENSE 120 bytes [emitted] js/list.a0c9911.js.gz 15.7 KiB [emitted] js/manifest.88160aa.js 3.12 KiB 2 [emitted] [immutable] manifest js/vendors.612571f.js 343 KiB 3 [emitted] [immutable] [big] vendors js/vendors.612571f.js.LICENSE 423 bytes [emitted] js/vendors.612571f.js.gz 97.7 KiB [emitted] js/work.3d8d43d.js 196 bytes 4 [emitted] [immutable] work
修改 app.css,然后再次打包代碼,打包結(jié)果如下,我們發(fā)現(xiàn) 除了 app.css hash 改變,app.js 的 hash 一樣的發(fā)生了改變,這又是為什么呢,通過試驗(yàn)是因?yàn)?CSS moduley 引起的問題,因?yàn)?css 文件的改變也會(huì)改變到 js,初步猜測(cè)是 css module 的問題,經(jīng)過試驗(yàn)發(fā)現(xiàn)即使去掉 cssMOdule 還是有同樣的問題
```javascript Asset Size Chunks Chunk Names css/app.29ae3c7.css 52 bytes 0 [emitted] [immutable] app css/list.8298c67.css 1.5 KiB 1 [emitted] [immutable] list css/vendors.353f491.css 71.2 KiB 3 [emitted] [immutable] vendors css/vendors.353f491.css.gz 7.85 KiB [emitted] index.html 1.4 KiB [emitted] js/app.3bafc2a.js 3.74 KiB 0 [emitted] [immutable] app js/list.a0c9911.js 51.5 KiB 1 [emitted] [immutable] list js/list.a0c9911.js.LICENSE 120 bytes [emitted] js/list.a0c9911.js.gz 15.7 KiB [emitted] js/manifest.88160aa.js 3.12 KiB 2 [emitted] [immutable] manifest js/vendors.612571f.js 343 KiB 3 [emitted] [immutable] [big] vendors js/vendors.612571f.js.LICENSE 423 bytes [emitted] js/vendors.612571f.js.gz 97.7 KiB [emitted] js/work.3d8d43d.js 196 bytes 4 [emitted] [immutable] work ```
后來一想,其實(shí)跟上面的 app.js 和 app.css hash 一樣是同樣的問題,app.js 的改變,就是會(huì)改變 chunk 的值,所以把修改 webpack 的配置如下
Asset Size Chunks Chunk Names css/app.44b7866.css 38 bytes 0 [emitted] [immutable] app css/list.8298c67.css 1.5 KiB 1 [emitted] [immutable] list css/vendors.353f491.css 71.2 KiB 3 [emitted] [immutable] vendors css/vendors.353f491.css.gz 7.85 KiB [emitted] index.html 1.4 KiB [emitted] js/app.ca738c5.js 3.67 KiB 0 [emitted] [immutable] app js/list.1895c1a.js 51.5 KiB 1 [emitted] [immutable] list js/list.1895c1a.js.LICENSE 120 bytes [emitted] js/list.1895c1a.js.gz 15.7 KiB [emitted] js/manifest.b7ee988.js 3.12 KiB 2 [emitted] [immutable] manifest js/vendors.38fec86.js 343 KiB 3 [emitted] [immutable] [big] vendors js/vendors.38fec86.js.LICENSE 423 bytes [emitted] js/vendors.38fec86.js.gz 97.7 KiB [emitted] js/work.ea3817c.js 196 bytes 4 [emitted] [immutable] work /* 修改css之后然后再次打包,果然解決了之前的問題 */ Asset Size Chunks Chunk Names css/app.208b221.css 39 bytes 0 [emitted] [immutable] app css/list.8298c67.css 1.5 KiB 1 [emitted] [immutable] list css/vendors.353f491.css 71.2 KiB 3 [emitted] [immutable] vendors css/vendors.353f491.css.gz 7.85 KiB [emitted] index.html 1.4 KiB [emitted] js/app.ca738c5.js 3.67 KiB 0 [emitted] [immutable] app js/list.1895c1a.js 51.5 KiB 1 [emitted] [immutable] list js/list.1895c1a.js.LICENSE 120 bytes [emitted] js/list.1895c1a.js.gz 15.7 KiB [emitted] js/manifest.b7ee988.js 3.12 KiB 2 [emitted] [immutable] manifest js/vendors.38fec86.js 343 KiB 3 [emitted] [immutable] [big] vendors js/vendors.38fec86.js.LICENSE 423 bytes [emitted] js/vendors.38fec86.js.gz 97.7 KiB [emitted] js/work.ea3817c.js 196 bytes 4 [emitted] [immutable] work /* 移除一個(gè)list引用的模塊,再次打包,完全符合預(yù)期 */ Asset Size Chunks Chunk Names css/app.208b221.css 39 bytes 0 [emitted] [immutable] app css/list.8298c67.css 1.5 KiB 1 [emitted] [immutable] list css/vendors.353f491.css 71.2 KiB 3 [emitted] [immutable] vendors css/vendors.353f491.css.gz 7.85 KiB [emitted] index.html 1.4 KiB [emitted] js/app.ca738c5.js 3.67 KiB 0 [emitted] [immutable] app js/list.271a546.js 51.5 KiB 1 [emitted] [immutable] list js/list.271a546.js.LICENSE 120 bytes [emitted] js/list.271a546.js.gz 15.7 KiB [emitted] js/manifest.a2e6ed1.js 3.12 KiB 2 [emitted] [immutable] manifest js/vendors.38fec86.js 343 KiB 3 [emitted] [immutable] [big] vendors js/vendors.38fec86.js.LICENSE 423 bytes [emitted] js/vendors.38fec86.js.gz 97.7 KiB [emitted] js/work.ea3817c.js 196 bytes 4 [emitted] [immutable] work
webpack5
webpack5 對(duì)moduleIds & chunkIds的優(yōu)化,不在是以數(shù)字作為id
optimization:{ moduleIds:'deterministic', chunkIds:'deterministic' },
如何使用 hash 做緩存呢?
很多人知道 hash,但是要不項(xiàng)目配置為 hash,不利于做長期緩存,要不前端配置好了,但是不知道如何配合后端做長期緩存,這就涉及到 http 緩存的
Etag - Last-Modified
1、客戶端請(qǐng)求一個(gè)頁面 A
2、服務(wù)器返回頁面 A,并在給 A 加上一個(gè) Last-Modified(Mon, 22 Mar 2018 10:10:10 GMT)和 ETag(2e681a-6-5d044840)
3、客戶端展現(xiàn)該頁面,并將頁面連同 Last-Modified/ETag 一起緩存
4、客戶再次請(qǐng)求頁面 A,并將上次請(qǐng)求時(shí)服務(wù)器返回的 Last-Modified/ETag 一起傳遞給服務(wù)器,也就是說發(fā)送 If-None-Match 頭,這個(gè)頭的內(nèi)容 就是 2e681a-6-5d044840,發(fā)送 If-Modified-Since(Mon, 22 Mar 2018 10:10:10 GMT)
5、服務(wù)器判斷發(fā)送過來的 Etag 和 Last-Modified 與本地匹配,如果沒有修改,不返回 200,返回 304,直接返回響應(yīng) 304 和一個(gè)空的響應(yīng)體,當(dāng)然響應(yīng)頭也會(huì)包含 Last-Modified(Mon, 22 Mar 2018 10:10:10 GMT)和 ETag(2e681a-6-5d044840)
Cache-control
Cache-control 判斷瀏覽器是否需要發(fā)送請(qǐng)求而不需要服務(wù)器對(duì)比,常見的取值有 private、no-cache、max-age、must- revalidate、no-store 等,默認(rèn)為 private,Cache-control 值為“no-cache”時(shí),訪問此頁面不會(huì)在 Internet 臨時(shí)文章夾留下頁面?zhèn)浞?/p>
打開新窗口
- 值為 private、no-cache、must-revalidate,那么打開新窗口訪問時(shí)都會(huì)重新訪問服務(wù)器。 而如果指定了 max-age 值,那么在此值內(nèi)的時(shí)間里就不會(huì)重新訪問服務(wù)器,例如: Cache-control: max-age=5(表示當(dāng)訪問此網(wǎng)頁后的 5 秒 內(nèi)再次訪問不會(huì)去服務(wù)器)
在地址欄回車
- 值為 private 或 must-revalidate 則只有第一次訪問時(shí)會(huì)訪問服務(wù)器,以后就不再訪問。 值為 no-cache,那么每次都會(huì)訪問。 值為 max-age,則在過期之前不會(huì)重復(fù)訪問
按后退按扭
- 值為 private、must-revalidate、max-age,則不會(huì)重訪問, 值為 no-cache,則每次都重復(fù)訪問
按刷新按扭或者 F6
- 無論為何值,都會(huì)重復(fù)訪問
Expires
Expires 和 max-age 都可以用來指定文檔的過期時(shí)間,但是也有不同
Expires 指定一個(gè)絕對(duì)的過期時(shí)間(GMT 格式)
max-age 指定的是從文檔被訪問后的存活時(shí)間,這個(gè)時(shí)間是個(gè)相對(duì)值(比如:3600s),相對(duì)的是文檔第一次被請(qǐng)求時(shí)服務(wù)器記錄的 Request_time(請(qǐng)求時(shí)間)
有的服務(wù)器, max-age 是這樣計(jì)算出來的,expires - request_time
靜態(tài)資源服務(wù)器的緩存
如果是第一次訪問,請(qǐng)求報(bào)文首部不會(huì)包含相關(guān)字段,服務(wù)端在發(fā)送文件前做如下處理
- 如服務(wù)器支持 ETag,設(shè)置 ETag 頭
如服務(wù)器支持 Last-Modified,設(shè)置 Last-Modified 頭
設(shè)置 Expires 頭 + 設(shè)置 Cache-Control 頭(設(shè)置其 max-age 值)瀏覽器收到響應(yīng)后會(huì)存下這些標(biāo)記,并在下次請(qǐng)求時(shí)帶上與 ETag 對(duì)應(yīng)的請(qǐng)求首部 If-None-Match 或與 Last-Modified 對(duì)應(yīng)的請(qǐng)求首部 If-Modified-Since
如果是重復(fù)的請(qǐng)求
瀏覽器判斷緩存是否過期(通過 Cache-Control 和 Expires 確定, 兩者都存在 Cache-Control為主)
如果未過期,直接使用緩存內(nèi)容,也就是強(qiáng)緩存命中,并不會(huì)產(chǎn)生新的請(qǐng)求
如果已過期,會(huì)發(fā)起新的請(qǐng)求,并且請(qǐng)求會(huì)帶上 If-None-Match 或 If-Modified-Since,或者兼具兩者(兩者都存在Etag 為主)
服務(wù)器收到請(qǐng)求,進(jìn)行緩存的新鮮度再驗(yàn)證:
首先檢查請(qǐng)求是否有 If-None-Match 首部,沒有則繼續(xù)下一步,有則將其值與文檔的最新 ETag 匹配,失敗則認(rèn)為緩存不新鮮,成功則繼續(xù)下一步
接著檢查請(qǐng)求是否有 If-Modified-Since 首部,沒有則保留上一步驗(yàn)證結(jié)果,有則將其值與文檔最新修改時(shí)間比較驗(yàn)證,失敗則認(rèn)為緩存不新鮮,成功則認(rèn)為緩存新鮮
當(dāng)兩個(gè)首部皆不存在或者驗(yàn)證結(jié)果是不新鮮時(shí),發(fā)送 200 及最新文件,并在首部更新新鮮度。
當(dāng)驗(yàn)證結(jié)果是緩存仍然新鮮時(shí)(也就是弱緩存命中),不需發(fā)送文件,僅發(fā)送 304,并在首部更新新鮮度
max-age 配合 hash 做使用
在保持 hash 不變性的前提下,我們可以使用 max-age 來設(shè)置前端緩存
/* 具體設(shè)置多少,個(gè)人覺得要看升級(jí)的頻率,在保證hash不變性的前提下,設(shè)置1y 比較合理https://expressjs.com/zh-cn/guide/using-middleware.html */ app.use( express.static(path.join(__dirname, "public"), { maxAge: "1y", expires: "1y", Etag: false, lastModified: false, }) );
參考文章
到此這篇關(guān)于一文搞懂webpack hash持久化的原理的文章就介紹到這了,更多相關(guān)webpack hash持久化內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
JavaScript取得gridview中獲取checkbox選中的值
這篇文章主要介紹了 js取得gridview中獲取checkbox選中的值,本文給大家分享兩段代碼片段,非常不錯(cuò),具有參考借鑒價(jià)值,需要的朋友可以參考下2017-07-07JavaScript數(shù)據(jù)結(jié)構(gòu)與算法之棧詳解
這篇文章主要介紹了JavaScript數(shù)據(jù)結(jié)構(gòu)與算法之棧詳解,本文講解了對(duì)棧的操作、對(duì)棧的實(shí)現(xiàn)實(shí)例等內(nèi)容,需要的朋友可以參考下2015-03-03JavaScript?ECMAScript?6(ES2015~ES2022)所有新特性總結(jié)
這篇文章主要介紹了JavaScript?ECMAScript?6(ES2015~ES2022)所有新特性總結(jié),文章圍繞主題展開詳細(xì)的內(nèi)容介紹,具有一定的參考價(jià)值,需要的小伙伴可以參考一下2022-07-07js+html5實(shí)現(xiàn)手機(jī)九宮格密碼解鎖功能
這篇文章主要為大家詳細(xì)介紹了js+html5實(shí)現(xiàn)手機(jī)九宮格密碼解鎖功能,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2018-07-07詳解JS實(shí)現(xiàn)系統(tǒng)登錄頁的登錄和驗(yàn)證
這篇文章主要介紹了JS實(shí)現(xiàn)系統(tǒng)登錄頁的登錄和驗(yàn)證,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2019-04-04基于pako.js實(shí)現(xiàn)gzip的壓縮和解壓功能示例
這篇文章主要介紹了基于pako.js實(shí)現(xiàn)gzip的壓縮和解壓功能,結(jié)合具體實(shí)例形式分析了pako.js實(shí)現(xiàn)字符串壓縮與解壓縮的相關(guān)操作技巧,需要的朋友可以參考下2017-06-06