一文搞懂webpack?hash持久化的原理
理解 module、chunk 和 bundle
module 就是我們通過 import 引入的各種模塊
chunk 是 webpack 根據(jù)功能拆分出來的模塊,包括入口文件, 動(dòng)態(tài) import,lazy 等的文件以及 splitChunks 拆分出來的代碼,chunk 可能包含多個(gè) module
bundle 就是 webpack 打包之后的各個(gè)文件,于 chunk 一般一一對應(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 對象代表某個(gè)版本的資源對應(yīng)的編譯進(jìn)程,當(dāng)我們的文件發(fā)生改變的時(shí)候, 進(jìn)而能夠針對改動(dòng)生產(chǎn)全新的編譯文件。compilation 對象包含當(dāng)前模塊資源、待編譯文件、有改動(dòng)的文件和監(jiān)聽依賴的所有信息,如果我們修改某一個(gè)文件,那么此時(shí)整個(gè)項(xiàng)目的 hash 都會改變
compiler
- compiler 對象代表的是配置完備的 Webpack 環(huán)境。 compiler 對象只在 Webpack 啟動(dòng)時(shí)構(gòu)建一次,由 Webpack 組合所有的配置項(xiàng)構(gòu)建生成,compiler 對象代表的是不變的 webpack 環(huán)境,compilation 是針對隨時(shí)可變的項(xiàng)目文件
module_id
- webpack 通過給每一個(gè)模塊一個(gè) module_id 來處理各個(gè)模塊之間的依賴關(guān)系,而默認(rèn)的 id 命名規(guī)則是根據(jù)模塊引入的順序賦予一個(gè)整數(shù)(1,2,3),所以任意的增添或者刪除一個(gè)模塊的依賴,都會對整個(gè)的 ID 序列產(chǎn)生影響,最后影響 hash 值,這些模塊會被 runtime 和 manifest 和引用到
對于圖片、字體、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í),它會保留所有模塊的詳細(xì)要點(diǎn)。這個(gè)數(shù)據(jù)集合稱為 "manifest",當(dāng)完成打包并發(fā)送到瀏覽器時(shí),runtime 會通過 manifest 來解析和加載模塊。無論你選擇哪種 模塊語法,那些 import 或 require 語句現(xiàn)在都已經(jīng)轉(zhuǎn)換為 webpack_require 方法,此方法指向模塊標(biāo)識符(module identifier)。通過使用 manifest 中的數(shù)據(jù),runtime 將能夠檢索這些標(biāo)識符,找出每個(gè)標(biāo)識符背后對應(yīng)的模塊
runtime 和 manifest 是一個(gè)每次打包都可能變化的不穩(wěn)定的因素,所以他會導(dǎo)致一些問題,比如,我們對整個(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 都不會改變
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 會基于默認(rèn)的解析順序(resolve order)進(jìn)行增量(類似于沒有指定 key 的 react 的組件的渲染)。也就是說,當(dāng)解析順序發(fā)生變化,ID 也會隨之改變,所以我們需要自己命名這個(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 會默認(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 每次都會產(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 的值就不會每次都發(fā)生變化了,此時(shí)我們對項(xiàng)目進(jìn)行打包,然后修改 work.js 我們會發(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 ```
對 app.css 做修改,然后重新打包,打包結(jié)果如下,我們發(fā)現(xiàn),app.css 的 hash 發(fā)生了變化,但是 app.js 的 hash 也發(fā)生了變化,這就是因?yàn)?app.css 和 app.js 屬于同一個(gè) chunk,所以這個(gè)時(shí)候我們就必須對 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 文件的改變也會改變到 js,初步猜測是 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 的改變,就是會改變 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 對moduleIds & chunkIds的優(yōu)化,不在是以數(shù)字作為id
optimization:{
moduleIds:'deterministic',
chunkIds:'deterministic'
},
如何使用 hash 做緩存呢?
很多人知道 hash,但是要不項(xiàng)目配置為 hash,不利于做長期緩存,要不前端配置好了,但是不知道如何配合后端做長期緩存,這就涉及到 http 緩存的
Etag - Last-Modified
1、客戶端請求一個(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、客戶再次請求頁面 A,并將上次請求時(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)頭也會包含 Last-Modified(Mon, 22 Mar 2018 10:10:10 GMT)和 ETag(2e681a-6-5d044840)
Cache-control
Cache-control 判斷瀏覽器是否需要發(fā)送請求而不需要服務(wù)器對比,常見的取值有 private、no-cache、max-age、must- revalidate、no-store 等,默認(rèn)為 private,Cache-control 值為“no-cache”時(shí),訪問此頁面不會在 Internet 臨時(shí)文章夾留下頁面?zhèn)浞?/p>
打開新窗口
- 值為 private、no-cache、must-revalidate,那么打開新窗口訪問時(shí)都會重新訪問服務(wù)器。 而如果指定了 max-age 值,那么在此值內(nèi)的時(shí)間里就不會重新訪問服務(wù)器,例如: Cache-control: max-age=5(表示當(dāng)訪問此網(wǎng)頁后的 5 秒 內(nèi)再次訪問不會去服務(wù)器)
在地址欄回車
- 值為 private 或 must-revalidate 則只有第一次訪問時(shí)會訪問服務(wù)器,以后就不再訪問。 值為 no-cache,那么每次都會訪問。 值為 max-age,則在過期之前不會重復(fù)訪問
按后退按扭
- 值為 private、must-revalidate、max-age,則不會重訪問, 值為 no-cache,則每次都重復(fù)訪問
按刷新按扭或者 F6
- 無論為何值,都會重復(fù)訪問
Expires
Expires 和 max-age 都可以用來指定文檔的過期時(shí)間,但是也有不同
Expires 指定一個(gè)絕對的過期時(shí)間(GMT 格式)
max-age 指定的是從文檔被訪問后的存活時(shí)間,這個(gè)時(shí)間是個(gè)相對值(比如:3600s),相對的是文檔第一次被請求時(shí)服務(wù)器記錄的 Request_time(請求時(shí)間)
有的服務(wù)器, max-age 是這樣計(jì)算出來的,expires - request_time
靜態(tài)資源服務(wù)器的緩存
如果是第一次訪問,請求報(bào)文首部不會包含相關(guān)字段,服務(wù)端在發(fā)送文件前做如下處理
- 如服務(wù)器支持 ETag,設(shè)置 ETag 頭
如服務(wù)器支持 Last-Modified,設(shè)置 Last-Modified 頭
設(shè)置 Expires 頭 + 設(shè)置 Cache-Control 頭(設(shè)置其 max-age 值)瀏覽器收到響應(yīng)后會存下這些標(biāo)記,并在下次請求時(shí)帶上與 ETag 對應(yīng)的請求首部 If-None-Match 或與 Last-Modified 對應(yīng)的請求首部 If-Modified-Since
如果是重復(fù)的請求
瀏覽器判斷緩存是否過期(通過 Cache-Control 和 Expires 確定, 兩者都存在 Cache-Control為主)
如果未過期,直接使用緩存內(nèi)容,也就是強(qiáng)緩存命中,并不會產(chǎn)生新的請求
如果已過期,會發(fā)起新的請求,并且請求會帶上 If-None-Match 或 If-Modified-Since,或者兼具兩者(兩者都存在Etag 為主)
服務(wù)器收到請求,進(jìn)行緩存的新鮮度再驗(yàn)證:
首先檢查請求是否有 If-None-Match 首部,沒有則繼續(xù)下一步,有則將其值與文檔的最新 ETag 匹配,失敗則認(rèn)為緩存不新鮮,成功則繼續(xù)下一步
接著檢查請求是否有 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è)人覺得要看升級的頻率,在保證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)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Js實(shí)現(xiàn)雙擊鼠標(biāo)自動(dòng)滾動(dòng)屏幕的示例代碼
這篇文章主要介紹了Js實(shí)現(xiàn)雙擊鼠標(biāo)自動(dòng)滾動(dòng)屏幕的示例代碼。需要的朋友可以過來參考下,希望對大家有所幫助2013-12-12
JavaScript模板引擎實(shí)現(xiàn)原理實(shí)例詳解
這篇文章主要介紹了JavaScript模板引擎實(shí)現(xiàn)原理,結(jié)合實(shí)例形式詳細(xì)分析了JavaScript模板引擎原理、定義、使用方法及相關(guān)操作注意事項(xiàng),需要的朋友可以參考下2018-12-12
JS實(shí)現(xiàn)的文字與圖片定時(shí)切換效果代碼
這篇文章主要介紹了JS實(shí)現(xiàn)的文字與圖片定時(shí)切換效果代碼,可實(shí)現(xiàn)定時(shí)切換及鼠標(biāo)點(diǎn)擊切換兩種效果,涉及JavaScript鼠標(biāo)事件控制頁面樣式的相關(guān)技巧,需要的朋友可以參考下2015-10-10
JS實(shí)現(xiàn)用戶注冊時(shí)獲取短信驗(yàn)證碼和倒計(jì)時(shí)功能
在用戶注冊時(shí),通常需要短信驗(yàn)證碼,而且為了交互效果,也需要增加倒計(jì)時(shí)。該如何實(shí)現(xiàn)獲取驗(yàn)證碼倒計(jì)時(shí)功能呢?下面小編給大家分享JS實(shí)現(xiàn)用戶注冊時(shí)獲取短信驗(yàn)證碼和倒計(jì)時(shí)的代碼,一起看看吧2016-10-10
Javascript+XMLHttpRequest+asp.net無刷新讀取數(shù)據(jù)庫數(shù)據(jù)
Javascript+XMLHttpRequest+asp.net無刷新讀取數(shù)據(jù)庫數(shù)據(jù)2009-08-08
前端項(xiàng)目中報(bào)錯(cuò)Uncaught?(in?promise)的解決方法
最近在做項(xiàng)目的時(shí)候控制臺報(bào)了一個(gè)錯(cuò)Uncaught(in promise) false,這篇文章主要給大家介紹了關(guān)于前端項(xiàng)目中報(bào)錯(cuò)Uncaught?(in?promise)的解決方法,文中通過實(shí)例代碼介紹的非常詳細(xì),需要的朋友可以參考下2023-04-04
JS實(shí)現(xiàn)的簡單下拉框聯(lián)動(dòng)功能示例
這篇文章主要介紹了JS實(shí)現(xiàn)的簡單下拉框聯(lián)動(dòng)功能,涉及javascript事件響應(yīng)及頁面元素屬性動(dòng)態(tài)修改相關(guān)操作技巧,需要的朋友可以參考下2018-05-05
JS實(shí)現(xiàn)密碼框的顯示密碼和隱藏密碼功能示例
這篇文章主要介紹了JS實(shí)現(xiàn)密碼框的顯示密碼和隱藏密碼功能,涉及javascript針對頁面form表單元素動(dòng)態(tài)操作的相關(guān)技巧,需要的朋友可以參考下2016-12-12

