溫馨提示
本文只是下面兩篇文章
HTTP緩存
Caching best practices & max-age gotchas
的閱讀理解佳遂。如有錯誤請不吝賜教蒋情!
前段時間看了Service Worker沙廉,今天看了HTTP緩存,既然都是緩存厂置,那么它們之間必定就有聯(lián)系厚宰。
HTTP緩存的內(nèi)容不多,總結(jié)成了一張思維導(dǎo)圖鲫竞,具體可以看這篇文章:
HTTP緩存
還有文章里一張很干貨的圖辐怕,什么時候用什么緩存策略:
關(guān)于緩存策略還可以參考這篇文章:
Caching best practices & max-age gotchas
文章主要思想如下:
- 不變的內(nèi)容,用大的max-age从绘,比如
Cache-Control: max-age=31536000
寄疏,當(dāng)文件發(fā)生修改,可以通過同步更新文件名(比如僵井,style-v1.css , style-v2.css)的方式在強(qiáng)制更新緩存陕截。
這種方法不適用于博客類的網(wǎng)頁,因為博文的url無法版本化批什。 - 經(jīng)常改變的內(nèi)容农曲,每次請求都要向服務(wù)器驗證其內(nèi)容沒變,
Cache-Control: no-cache
- 適當(dāng)使用
max-age
會有效減輕服務(wù)器壓力渊季,但有時把max-age
用在經(jīng)常改變的內(nèi)容上可能會有很嚴(yán)重的后果朋蔫。
舉個例子,現(xiàn)在HTTP緩存了index.html
,style.css
,script.js
却汉,在新鮮期內(nèi)驯妄,緩存器丟失了style.css
,而服務(wù)器剛好又更新了這三個文件合砂。
現(xiàn)在我請求這三個文件青扔,緩存器會返回緩存的index.html
,script.js
,因為style.css
丟了,所以他要向服務(wù)器請求新的css文件微猖。
那么谈息,現(xiàn)在,我得到了舊的html凛剥,js文件侠仇,新的css文件,會出現(xiàn)什么情況呢犁珠?
整張頁面的樣式可能全亂掉了逻炊。
更可怕的是,因為三者的max-age
是一樣的犁享,那么緩存器請求了新的css文件余素,css文件的到期時間和html,js文件就變得不同步了炊昆!這樣的連鎖反應(yīng)導(dǎo)致的后果就是桨吊,以后緩存器中的style.css
和index.html
,script.js
可能會一直保持早不兼容的狀態(tài)。 - 如何解決這個問題呢凤巨?一種方法是用戶刷新頁面视乐,因為刷新頁面會繞過
max-age
,向服務(wù)器重新驗證內(nèi)容是否為最新磅甩。但是這么做會降低用戶對網(wǎng)站的好感度炊林。 - 第二種方法是和Service Worker合作。
終于進(jìn)入正題了~~卷要!
我們可能會想要用SW來代替HTTP緩存,比如現(xiàn)在寫一個這樣的sw.js
:
const version = '2';
self.addEventListener('install', event => {
event.waitUntil(
caches.open(`static-${version}`)
.then(cache => cache.addAll([
'/styles.css',
'/script.js'
]))
);
});
self.addEventListener('activate', event => {
// …delete old caches…
});
self.addEventListener('fetch', event => {
event.respondWith(
caches.match(event.request)
.then(response => response || fetch(event.request))
);
});
這段代碼說独榴,每次更新SW僧叉,SW在install階段中,會緩存/styles.css
, /script.js
兩個文件棺榔,之后會捕獲用戶的網(wǎng)絡(luò)請求瓶堕,先去找緩存里有沒有對象的響應(yīng),沒有的話再走正常的網(wǎng)絡(luò)流程症歇。
但是很尷尬的是郎笆,也許你已經(jīng)從前面的“正常的網(wǎng)絡(luò)流程”的字里行間看出來了,如果SW發(fā)送了一個網(wǎng)絡(luò)請求忘晤,請求也會經(jīng)過緩存服務(wù)器宛蚓,緩存服務(wù)器很可能會返回緩存的文件。也就是說设塔,我們還是會得到不兼容的版本凄吏。
同樣的道理,SW在install階段緩存的這兩個文件,也很有可能是不兼容的舊版本痕钢。
那我們要怎么避免這種情況图柏?一個辦法是讓SW每次發(fā)送的url都不一樣,這樣就繞過了HTTP緩存了任连。
self.addEventListener('install', event => {
event.waitUntil(
caches.open(`static-${version}`)
.then(cache => Promise.all(
[
'/styles.css',
'/script.js'
].map(url => {
// url中添加一個隨機(jī)數(shù)
return fetch(`${url}?${Math.random()}`).then(response => {
// fail on 404, 500 etc
if (!response.ok) throw Error('Not ok');
return cache.put(url, response);
})
})
))
);
});
有一些瀏覽器已經(jīng)支持了Request接口的cache選項蚤吹,可以直接這么寫:
self.addEventListener('install', event => {
event.waitUntil(
caches.open(`static-${version}`)
.then(cache => cache.addAll([
new Request('/styles.css', { cache: 'no-cache' }),
new Request('/script.js', { cache: 'no-cache' })
]))
);
});
我們似乎已經(jīng)解決了問題,但是随抠,用SW代替HTTP緩存可行嗎距辆?‘
看一下SW的兼容性,是不是淚流滿面暮刃?因為這種方案只能解決支持SW的瀏覽器的問題跨算。
更好的方法是兩者結(jié)合。
在HTTP緩存中椭懊,把根頁面的緩存機(jī)制設(shè)為:no-cache
诸蚕,css,js文件用版本號控制變更氧猬,然后使用max-age=...
的緩存策略背犯。每次我們更新css或者js文件,文件名也會改變盅抚。
使用下面的sw.js代碼:
const version = '23';
self.addEventListener('install', event => {
event.waitUntil(
caches.open(`static-${version}`)
.then(cache => cache.addAll([
'/',
'/script-f93bca2c.js',
'/styles-a837cb1e.css',
'/cats-0e9a2ef4.jpg'
]))
);
});
每次SW更新時漠魏,都會觸發(fā)一次對根頁面的請求。而css妄均,js文件只有在文件名改變時柱锹,才會打擾遠(yuǎn)程服務(wù)器。
如此丰包,我們避免了版本不兼容的問題禁熏,也有效的節(jié)省了帶寬。