自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

前端靜態(tài)資源緩存最優(yōu)解以及max-age的陷阱

開發(fā) 前端
合理的使用緩存可以極大地提高網(wǎng)站的性能優(yōu)勢,還可以節(jié)約帶寬從而降低服務(wù)器成本。但是很多站點(diǎn)有只弄對(duì)了一半或者一半都沒有,如果是這樣,就完全沒有發(fā)揮出緩存的優(yōu)勢。很大程度上產(chǎn)生會(huì)由于靜態(tài)資源的競爭關(guān)系而導(dǎo)致依賴的靜態(tài)資源不同步。

合理的使用緩存可以極大地提高網(wǎng)站的性能優(yōu)勢,還可以節(jié)約帶寬從而降低服務(wù)器成本。但是很多站點(diǎn)有只弄對(duì)了一半或者一半都沒有,如果是這樣,就完全沒有發(fā)揮出緩存的優(yōu)勢。很大程度上產(chǎn)生會(huì)由于靜態(tài)資源的競爭關(guān)系而導(dǎo)致依賴的靜態(tài)資源不同步。

以下為兩個(gè)***靜態(tài)資源緩存實(shí)踐的例子。

一、資源內(nèi)容不變 + 設(shè)置長時(shí)間max-age

 

  1. // 設(shè)置緩存時(shí)間為1年  
  2. Cache-Control: max-age=31536000 

 

  • 資源的內(nèi)容不會(huì)更改,所以。。。
  • 瀏覽器/CDN可以緩存一年時(shí)間,在這期間資源不會(huì)出現(xiàn)問題。
  • 可以在不請求服務(wù)器的請情況下,一年內(nèi)都使用緩存內(nèi)容。

***天

瀏覽器請求了/index-v1.js、/base-v1.css以及/dog-v1.png這三個(gè)資源。

 

第二天

這次瀏覽器請求了/index-v2.js、/base-v2.css以及/dog-v1.png這三個(gè)資源。

此處注意:index.js和base.css與***天請求的版本號(hào)不同。

過了一年

在一年的時(shí)間里,瀏覽器再也沒有請求過/index-v1.js、/base-v1.css以及/dog-v1.png這三個(gè)資源,瀏覽器緩存就會(huì)把它們給刪掉。

 

所以在這個(gè)例子中,為了讓緩存發(fā)揮***效率,你要做的并不是更改文件的內(nèi)容,而是應(yīng)該更改資源的URL:

 

  1. <script src="/index-v3.js"></script>  
  2. <link rel="stylesheet" href="/base-v3.css" 
  3. <img src="/dog-v3.jpg" alt="…"

 

每一個(gè)靜態(tài)資源URL都應(yīng)該跟隨其內(nèi)容的修改而改變。例如示例index-v1.js中的v1,你對(duì)它的命名不需要有任何限制。它可以是一個(gè)版本號(hào),***修改的日期,或者根據(jù)內(nèi)容計(jì)算出來的散列值。

絕大多數(shù)服務(wù)器端的框架都提供了工具來實(shí)現(xiàn)這一點(diǎn),同樣的在nodejs中有很多優(yōu)秀的庫來實(shí)現(xiàn)這個(gè)功能,比如gulp-rev、webpack、fis3。

二、對(duì)于經(jīng)常修改的內(nèi)容,始終需要進(jìn)行服務(wù)器認(rèn)證

 

  1. Cache-Control: no-cache 

該URL下資源的內(nèi)容可能經(jīng)常修改,所以。。。

沒有服務(wù)器的確認(rèn),任何本地緩存的版本內(nèi)容都是不可信的。

***天

第二天

 

注意:

no-cache并不意味著不緩存。它的意思是在使用緩存資源之前,它必須經(jīng)過服務(wù)器的檢查(revalidate也可以實(shí)現(xiàn)這個(gè)功能)。

no-store才是告訴瀏覽器不要緩存它。此外,must-revalidate并不意味著必須重新認(rèn)證,它的前提是資源還在max-age的緩存期內(nèi),否則必須重新認(rèn)證。

在此模式下 ,你也可以將ETag(你選擇的版本ID)或者Last-modified日期添加到響應(yīng)首部中??蛻舳讼麓潍@取資源時(shí),他會(huì)分別通過If-None-Match(與ETage對(duì)應(yīng))和If-Modified-Since(與Last-Mofied對(duì)應(yīng))兩個(gè)請求首部將值發(fā)送給服務(wù)器。如果服務(wù)器發(fā)現(xiàn)兩次值都是對(duì)等的,就是返回一個(gè)HTTP 304。

如果沒有發(fā)送ETag和Last-Modified,那么服務(wù)器將始終返回完整的資源內(nèi)容。

但是這種方法有個(gè)缺點(diǎn),就是它每次都會(huì)去服務(wù)器做一次驗(yàn)證,涉及到了網(wǎng)絡(luò)提取,所以它不如***個(gè)例子那樣可以完全繞過網(wǎng)絡(luò)。

三、在經(jīng)常修改內(nèi)容的靜態(tài)資源上使用max-age是個(gè)錯(cuò)誤的選擇

這種情況并不少見,例如它就實(shí)實(shí)在在地發(fā)生在了github的頁面上。

想象一下 :

  • /article/
  • /styles.css
  • /script.js

它們?nèi)渴褂玫氖牵?/p>

 

  1. // 十分鐘內(nèi)不需要重新認(rèn)證,超過十分鐘就需要重新認(rèn)證  
  2. Cache-Control: must-revalidate, max-age=600 

 

  • 隨著內(nèi)容的修改,URLs發(fā)生改變
  • 在十分鐘內(nèi),瀏覽器將會(huì)一直使用緩存住的內(nèi)容,而不會(huì)去服務(wù)器請求***的資源 。
  • 超過十分鐘,在可用的前提下使用If-Modified-Since和If-None-Match重新進(jìn)行服務(wù)器認(rèn)證。

***次請求:

 

六七分鐘過后:

 

最終:

 

[[225520]]

這種情況在測試中經(jīng)常出現(xiàn)。但是想象一下,在線上環(huán)境你永遠(yuǎn)不知道瀏覽器前面坐著的是什么樣的人,他很有可能無意中胡亂地用鼠標(biāo)點(diǎn)點(diǎn)點(diǎn),就打亂了瀏覽器的靜態(tài)資源緩存機(jī)制,導(dǎo)致頁面發(fā)生了錯(cuò)亂,而且真的很難追蹤。

在上面的例子中,服務(wù)器實(shí)際上已經(jīng)更新了HTML、CSS和JS,但是頁面***使用的是緩存中舊的HTML和JS,以及剛從服務(wù)器下載的***的CSS。多個(gè)靜態(tài)資源版本之間不匹配的問題隨之出現(xiàn)。

通常,當(dāng)我們對(duì)HTML進(jìn)行重大修改時(shí),我們可能會(huì)更改CSS文件來適配新的DOM結(jié)構(gòu),并且更新JS來配置樣式和DOM的修改。這些資源都是相互依賴的,但攜帶緩存信息的HTTP首部可不管你這些有的沒的。最終,用戶很有可能會(huì)得到一個(gè)/兩個(gè)靜態(tài)資源新版本,而其他資源都是舊版本。

max-age是相對(duì)于服務(wù)器響應(yīng)時(shí)間的,所以如果所有上述資源都在同一時(shí)間請求,即便它們都被設(shè)置為了相同的max-age時(shí)長,它們?nèi)匀淮嬖诤苄〉母偁幙赡苄裕ó吘褂械馁Y源先返回有的資源后返回)。如果你的某些頁面不包含JS,或者包含不同的CSS,它們的緩存失效時(shí)間就有可能會(huì)不同步。更惡心的是,瀏覽器始終會(huì)從緩存中刪除和獲取資源,它并不知道這些資源中哪個(gè)是相互依賴的,只要過了緩存時(shí)間它就會(huì)毫不猶豫地刪掉一個(gè),并不會(huì)刪掉這個(gè)過期文件所依賴的其他資源。把上面的種種可能性加在一起,就會(huì)大概率出現(xiàn)靜態(tài)資源版本不匹配的問題。

不過還好,我們還有法子來解決這個(gè)問題:

強(qiáng)制刷新瀏覽器或者清除緩存

在強(qiáng)制刷新瀏覽器或者清除緩存后,請求的頁面以及頁面內(nèi)的所有資源會(huì)忽略之前的max-age,去服務(wù)器做重新認(rèn)證。因此,如果用戶由于max-age出現(xiàn)問題之后,只需要強(qiáng)制刷新或者清緩存就可以修復(fù)問題。當(dāng)然,強(qiáng)迫用戶這樣做只會(huì)讓它們降低對(duì)你網(wǎng)站的信任度,認(rèn)為你的網(wǎng)站不靠譜。。。

使用serviceWorker減少這種錯(cuò)誤的出現(xiàn)幾率

service Worker的執(zhí)行時(shí)機(jī):

 

注冊serviceWorker:

 

  1. if (navigator.serviceWorker) {  
  2.   navigator.serviceWorker.register('/serviceworker.js', {  
  3.     scope: '/'  
  4.   });  

 

執(zhí)行serviceworker.js:

 

  1. const version = '2'  
  2. self.addEventListener('install', event => {  
  3.   // 由于系統(tǒng)會(huì)隨時(shí)睡眠SW,所以,為了防止執(zhí)行中斷,就需要使用 event.waitUntil 進(jìn)行捕獲  
  4.   event.waitUntil(  
  5.     caches.open(`static-${version}`)  
  6.     .then(cache => cache.addAll(  
  7.         // 不穩(wěn)定文件或者大文件加載  
  8.         //...  
  9.       ), cache.addAll([  
  10.       // 穩(wěn)定文件或小文件加載  
  11.       '/styles.css' 
  12.       '/script.js'  
  13.     ]));  
  14.   );  
  15. });  
  16.  
  17. self.addEventListener('activate', event => {  
  18.   // …delete old caches…  
  19. });  
  20.  
  21. self.addEventListener('fetch', event => {  
  22.   event.respondWith(  
  23.     caches.match(event.request)  
  24.     .then(response => response || fetch(event.request))  
  25.   );  
  26. }); 

 

  • 將script和styles緩存起來。
  • 如果有匹配到的緩存就從緩存中獲取,如果沒有就從服務(wù)器獲取。

如果我們修改了JS/CSS,只需修改version就可以讓service worker觸發(fā)更新。

你也可以在service worker中跳過緩存:

 

  1. self.addEventListener('install', event => {  
  2.     event.waitUntil(  
  3.         caches.open(`static-${version}`)  
  4.         .then(cache => cache.addAll([  
  5.             new Request('/styles.css', {  
  6.                 cache: 'no-cache'  
  7.             }),  
  8.             new Request('/script.js', {  
  9.                 cache: 'no-cache'  
  10.             })  
  11.         ]))  
  12.     );  
  13. }); 

 

不過很不巧的是,cache選項(xiàng)在和safari和opera中都不支持 ,只有firefox和chrome最近才開始支持。但是你可以這樣做:

 

  1. self.addEventListener('install', event => {  
  2.     event.waitUntil(  
  3.         caches.open(`static-${version}`)  
  4.         .then(cache => Promise.all 
  5.             [  
  6.                 '/styles.css' 
  7.                 '/script.js'  
  8.             ].map(url => {  
  9.                 // cache-bust using a random query string  
  10.                 return fetch(`${url}?${Math.random()}`).then(response => {  
  11.                     // fail on 404, 500 etc  
  12.                     if (!response.ok) throw Error('Not ok');  
  13.                     return cache.put(url, response);  
  14.                 })  
  15.             })  
  16.         ))  
  17.     );  
  18. }); 

 

你可以使用上面代碼中的隨機(jī)字符串,也可以使用散列值。這有點(diǎn)像在javascript中實(shí)現(xiàn)文章剛開始***小節(jié)的方法,不過僅僅是在server worker中使用。

四、service worker和HTTP cache也可以很好的共存

通過上個(gè)的例子,你可以看到service worker可以很好的處理一些糟糕的緩存情況。但是僅僅是做一些hack處理而已,最重要的是再根源上解決問題。正確的使用緩存不僅可以更好地使用service worker,還可以很好地在那些不支持service worker的瀏覽器(IE/Safari/Opera)上提高網(wǎng)站的性能。除此之外,對(duì)你的CDN也是大有益處。

正確的使用緩存,可以大量簡化service worker的代碼:

 

  1. const version = '23' 
  2. self.addEventListener('install', event => {  
  3.     event.waitUntil(  
  4.         caches.open(`static-${version}`)  
  5.         .then(cache => cache.addAll([  
  6.             '/' 
  7.             '/script-v3.js' 
  8.             '/styles-v3.css' 
  9.             '/dog-v3.jpg'  
  10.         ]))  
  11.     );  
  12. }); 

 

所以,我們可以使用第二小節(jié)的方法(服務(wù)器重新認(rèn)證)來緩存根HTML頁面。并使用***小節(jié)的方法(不同的內(nèi)容使用不同的URL)來緩存其他資源。每次service worker更新世都會(huì)去請求網(wǎng)站的根HTML頁面,其他資源只有在更改URL時(shí)才會(huì)去下載,從而提高網(wǎng)站的性能。

雖然service worker擅長提高網(wǎng)站的性能,但它并不是一個(gè)完整的解決方案。因此要和HTTP cache配合使用才可以顯著地提高性能。

五、max-age和『內(nèi)容經(jīng)常修改但是URL不變的靜態(tài)資源』搭配使用

在內(nèi)容經(jīng)常修改但是URL不變的靜態(tài)資源上使用max-age在通常意義上來說不是一個(gè)好點(diǎn)子,但事實(shí)卻不總是如此。

假如一個(gè)頁面的max-age為三分鐘,并且在這個(gè)頁面上不需要考慮靜態(tài)資源的競爭關(guān)系(靜態(tài)資源之間存在相互依賴,見第三小節(jié)),所以在這個(gè)頁面上不存在任何的靜態(tài)資源依賴。在這種情況下就可以盡情使用max-age。不過這也意味著網(wǎng)站的修改要再三分鐘之后才可以被看到。

不過要是頁面存在靜態(tài)資源競爭關(guān)系的話,這種法子不好用了,比如我現(xiàn)在有兩個(gè)文章A和B,我現(xiàn)在文章A中添加一個(gè)新的章節(jié),然后在文章B中增加了一個(gè)指向文章A新增章節(jié)的超鏈接。然后我從文章B中訪問這個(gè)鏈接,假如文章A的max-age沒有過期,那么我訪問到的文章A里將會(huì)發(fā)現(xiàn)文章并沒有那個(gè)新增的章節(jié)。此時(shí)只能等max-age過期或者強(qiáng)制刷新瀏覽器,再或者清除緩存了。所以,一定要謹(jǐn)慎使用這種方法。

 

正確使用緩存可以代理巨大的性能收益并且有效節(jié)省服務(wù)器帶寬。既支持版本號(hào)類型的靜態(tài)資源緩存方式也支持服務(wù)器重新認(rèn)證(no-cache、304)的方式。如果你覺得自己很勇敢,那么大可混合使用max-age和『內(nèi)容經(jīng)常修改但是URL不變的靜態(tài)資源』,但是前提你得確定自己的HTML中沒有靜態(tài)資源競爭關(guān)系。 

責(zé)任編輯:龐桂玉 來源: segmentfault
相關(guān)推薦

2009-07-21 11:14:19

max-age設(shè)置ASP.NET 2.0

2018-07-10 15:30:42

Go前端更新

2024-03-06 08:25:31

Compose開發(fā)界面

2024-04-03 14:26:08

2025-04-25 08:40:00

橋接網(wǎng)絡(luò)NAT網(wǎng)絡(luò)網(wǎng)絡(luò)

2011-05-24 16:58:52

CC++

2022-10-18 18:33:40

云原生數(shù)字化轉(zhuǎn)型

2023-12-04 09:37:00

C++靜態(tài)變量

2022-07-28 12:17:36

C語言typedef#define

2016-02-23 09:23:50

swift陷阱解決方法

2025-01-07 00:00:10

2022-12-01 11:40:05

模塊化數(shù)據(jù)中心

2022-09-19 09:19:24

云存儲(chǔ)TCO云服務(wù)

2021-04-13 16:18:30

人工智能強(qiáng)化學(xué)習(xí)人臉識(shí)別

2011-09-06 14:08:22

IIS

2023-10-04 18:24:54

wpf動(dòng)態(tài)資源

2018-07-31 10:56:28

2021-07-27 15:56:28

MaxCompute 資源優(yōu)化

2015-03-03 13:28:21

實(shí)例動(dòng)態(tài)網(wǎng)頁靜態(tài)緩存

2023-11-20 08:12:15

點(diǎn)贊
收藏

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