在工作中,前端代碼打包之后的生成的靜態(tài)資源就要發(fā)布到靜態(tài)服務(wù)器上,這時(shí)候就要做對(duì)這些靜態(tài)資源做一些運(yùn)維配置,其中,gzip和設(shè)置緩存是必不可少的。這兩項(xiàng)是最直接影響到網(wǎng)站性能和用戶(hù)體驗(yàn)的。
緩存的優(yōu)點(diǎn):
減少了不必要的數(shù)據(jù)傳輸,節(jié)省帶寬
減少服務(wù)器的負(fù)擔(dān),提升網(wǎng)站性能
加快了客戶(hù)端加載網(wǎng)頁(yè)的速度
用戶(hù)體驗(yàn)友好
缺點(diǎn):
資源如果有更改但是客戶(hù)端不及時(shí)更新會(huì)造成用戶(hù)獲取信息滯后,如果老版本有bug的話(huà),情況會(huì)更加糟糕。
所以,為了避免設(shè)置緩存錯(cuò)誤,掌握緩存的原理對(duì)于我們工作中去更加合理的配置緩存是非常重要的。
一、強(qiáng)緩存
到底什么是強(qiáng)緩存?強(qiáng)在哪?其實(shí)強(qiáng)是強(qiáng)制的意思。當(dāng)瀏覽器去請(qǐng)求某個(gè)文件的時(shí)候,服務(wù)端就在respone header里面對(duì)該文件做了緩存配置。緩存的時(shí)間、緩存類(lèi)型都由服務(wù)端控制,具體表現(xiàn)為:
respone header 的cache-control,常見(jiàn)的設(shè)置是max-age public private no-cache no-store等
如下圖,
設(shè)置了cache-control:max-age=31536000,public,immutable

image.png
max-age表示緩存的時(shí)間是31536000秒(1年),public表示可以被瀏覽器和代理服務(wù)器緩存,代理服務(wù)器一般可用nginx來(lái)做。immutable表示該資源永遠(yuǎn)不變,但是實(shí)際上該資源并不是永遠(yuǎn)不變,它這么設(shè)置的意思是為了讓用戶(hù)在刷新頁(yè)面的時(shí)候不要去請(qǐng)求服務(wù)器!啥意思?就是說(shuō),如果你只設(shè)置了cahe-control:max-age=31536000,public? 這屬于強(qiáng)緩存,每次用戶(hù)正常打開(kāi)這個(gè)頁(yè)面,瀏覽器會(huì)判斷緩存是否過(guò)期,沒(méi)有過(guò)期就從緩存中讀取數(shù)據(jù);但是有一些 "聰明" 的用戶(hù)會(huì)點(diǎn)擊瀏覽器左上角的刷新按鈕去刷新頁(yè)面,這時(shí)候就算資源沒(méi)有過(guò)期(1年沒(méi)這么快過(guò)),瀏覽器也會(huì)直接去請(qǐng)求服務(wù)器,這就是額外的請(qǐng)求消耗了,這時(shí)候就相當(dāng)于是走協(xié)商緩存的流程了(下面會(huì)講到)。如果cahe-control:max-age=315360000,public再加個(gè)immutable的話(huà),就算用戶(hù)刷新頁(yè)面,瀏覽器也不會(huì)發(fā)起請(qǐng)求去服務(wù),瀏覽器會(huì)直接從本地磁盤(pán)或者內(nèi)存中讀取緩存并返回200狀態(tài),看上圖的紅色框(from memory cache)。這是2015年facebook團(tuán)隊(duì)向制定 HTTP 標(biāo)準(zhǔn)的 IETF 工作組提到的建議:他們希望 HTTP 協(xié)議能給 Cache-Control 響應(yīng)頭增加一個(gè)屬性字段表明該資源永不過(guò)期,瀏覽器就沒(méi)必要再為這些資源發(fā)送條件請(qǐng)求了。
強(qiáng)緩存總結(jié)
cache-control: max-age=xxxx,public
客戶(hù)端和代理服務(wù)器都可以緩存該資源;
客戶(hù)端在xxx秒的有效期內(nèi),如果有請(qǐng)求該資源的需求的話(huà)就直接讀取緩存,statu code:200 ,如果用戶(hù)做了刷新操作,就向服務(wù)器發(fā)起http請(qǐng)求
cache-control: max-age=xxxx,private
只讓客戶(hù)端可以緩存該資源;代理服務(wù)器不緩存
客戶(hù)端在xxx秒內(nèi)直接讀取緩存,statu code:200
cache-control: max-age=xxxx,immutable
客戶(hù)端在xxx秒的有效期內(nèi),如果有請(qǐng)求該資源的需求的話(huà)就直接讀取緩存,statu code:200 ,即使用戶(hù)做了刷新操作,也不向服務(wù)器發(fā)起http請(qǐng)求
cache-control: no-cache
跳過(guò)設(shè)置強(qiáng)緩存,但是不妨礙設(shè)置協(xié)商緩存;一般如果你做了強(qiáng)緩存,只有在強(qiáng)緩存失效了才走協(xié)商緩存的,設(shè)置了no-cache就不會(huì)走強(qiáng)緩存了,每次請(qǐng)求都回詢(xún)問(wèn)服務(wù)端。
cache-control: no-store
不緩存,這個(gè)會(huì)讓客戶(hù)端、服務(wù)器都不緩存,也就沒(méi)有所謂的強(qiáng)緩存、協(xié)商緩存了。
二、協(xié)商緩存
上面說(shuō)到的強(qiáng)緩存就是給資源設(shè)置個(gè)過(guò)期時(shí)間,客戶(hù)端每次請(qǐng)求資源時(shí)都會(huì)看是否過(guò)期;只有在過(guò)期才會(huì)去詢(xún)問(wèn)服務(wù)器。所以,強(qiáng)緩存就是為了給客戶(hù)端自給自足用的。而當(dāng)某天,客戶(hù)端請(qǐng)求該資源時(shí)發(fā)現(xiàn)其過(guò)期了,這是就會(huì)去請(qǐng)求服務(wù)器了,而這時(shí)候去請(qǐng)求服務(wù)器的這過(guò)程就可以設(shè)置協(xié)商緩存。這時(shí)候,協(xié)商緩存就是需要客戶(hù)端和服務(wù)器兩端進(jìn)行交互的。
怎么設(shè)置協(xié)商緩存?
response header里面的設(shè)置
etag: '5c20abbd-e2e8'last-modified: Mon, 24 Dec 2018 09:49:49 GMT
etag:每個(gè)文件有一個(gè),改動(dòng)文件了就變了,就是個(gè)文件hash,每個(gè)文件唯一,就像用webpack打包的時(shí)候,每個(gè)資源都會(huì)有這個(gè)東西,如: app.js打包后變?yōu)?app.c20abbde.js,加個(gè)唯一hash,也是為了解決緩存問(wèn)題。
last-modified:文件的修改時(shí)間,精確到秒
也就是說(shuō),每次請(qǐng)求返回來(lái) response header 中的 etag和 last-modified,在下次請(qǐng)求時(shí)在 request header 就把這兩個(gè)帶上,服務(wù)端把你帶過(guò)來(lái)的標(biāo)識(shí)進(jìn)行對(duì)比,然后判斷資源是否更改了,如果更改就直接返回新的資源,和更新對(duì)應(yīng)的response header的標(biāo)識(shí)etag、last-modified。如果資源沒(méi)有變,那就不變etag、last-modified,這時(shí)候?qū)蛻?hù)端來(lái)說(shuō),每次請(qǐng)求都是要進(jìn)行協(xié)商緩存了,即:
發(fā)請(qǐng)求-->看資源是否過(guò)期-->過(guò)期-->請(qǐng)求服務(wù)器-->服務(wù)器對(duì)比資源是否真的過(guò)期-->沒(méi)過(guò)期-->返回304狀態(tài)碼-->客戶(hù)端用緩存的老資源。
這就是一條完整的協(xié)商緩存的過(guò)程。
當(dāng)然,當(dāng)服務(wù)端發(fā)現(xiàn)資源真的過(guò)期的時(shí)候,會(huì)走如下流程:
發(fā)請(qǐng)求-->看資源是否過(guò)期-->過(guò)期-->請(qǐng)求服務(wù)器-->服務(wù)器對(duì)比資源是否真的過(guò)期-->過(guò)期-->返回200狀態(tài)碼-->客戶(hù)端如第一次接收該資源一樣,記下它的cache-control中的max-age、etag、last-modified等。
所以協(xié)商緩存步驟總結(jié):
請(qǐng)求資源時(shí),把用戶(hù)本地該資源的 etag 同時(shí)帶到服務(wù)端,服務(wù)端和最新資源做對(duì)比。
如果資源沒(méi)更改,返回304,瀏覽器讀取本地緩存。
如果資源有更改,返回200,返回最新的資源。
補(bǔ)充一點(diǎn),response header中的etag、last-modified在客戶(hù)端重新向服務(wù)端發(fā)起請(qǐng)求時(shí),會(huì)在request header中換個(gè)key名:
// response headeretag:'5c20abbd-e2e8'last-modified:Mon,24Dec201809:49:49GMT// request header 變?yōu)閕f-none-matched:'5c20abbd-e2e8'if-modified-since:Mon,24Dec201809:49:49GMT
為什么要有etag?
你可能會(huì)覺(jué)得使用last-modified已經(jīng)足以讓瀏覽器知道本地的緩存副本是否足夠新,為什么還需要etag呢?HTTP1.1中etag的出現(xiàn)(也就是說(shuō),etag是新增的,為了解決之前只有If-Modified的缺點(diǎn))主要是為了解決幾個(gè)last-modified比較難解決的問(wèn)題:
一些文件也許會(huì)周期性的更改,但是他的內(nèi)容并不改變(僅僅改變的修改時(shí)間),這個(gè)時(shí)候我們并不希望客戶(hù)端認(rèn)為這個(gè)文件被修改了,而重新get;
某些文件修改非常頻繁,比如在秒以下的時(shí)間內(nèi)進(jìn)行修改,(比方說(shuō)1s內(nèi)修改了N次),if-modified-since能檢查到的粒度是秒級(jí)的,這種修改無(wú)法判斷(或者說(shuō)UNIX記錄MTIME只能精確到秒);
某些服務(wù)器不能精確的得到文件的最后修改時(shí)間。
怎么設(shè)置強(qiáng)緩存與協(xié)商緩存
后端服務(wù)器如nodejs:
res.setHeader('max-age': '3600 public')
res.setHeader(etag: '5c20abbd-e2e8')
res.setHeader('last-modified': Mon, 24 Dec 2018 09:49:49 GMT)
nginx配置

image.png
偶爾自己折騰一番非前端的東西時(shí),若心中有數(shù),自然不會(huì)手忙腳亂。
怎么去用?
舉個(gè)例子,像目前用vue-cli打包后生成的單頁(yè)文件是有一個(gè)html,與及一堆js css img資源,怎么去設(shè)置這些文件呢,核心需求是
要有緩存,毋庸置疑
當(dāng)發(fā)新包的時(shí)候,要避免加載老的緩存資源

打包好的靜態(tài)文件
我的做法是:
index.html文件采用協(xié)商緩存,理由就是要用戶(hù)每次請(qǐng)求index.html不拿瀏覽器緩存,直接請(qǐng)求服務(wù)器,這樣就保證資源更新了,用戶(hù)能馬上訪(fǎng)問(wèn)到新資源,如果服務(wù)端返回304,這時(shí)候再拿瀏覽器的緩存的index.html,切記不要設(shè)置強(qiáng)緩存!!!
其他資源采用強(qiáng)緩存 + 協(xié)商緩存,理由就不多說(shuō)了。
作者:_小_七_(dá)
鏈接:http://www.itdecent.cn/p/9c95db596df5
來(lái)源:簡(jiǎn)書(shū)
著作權(quán)歸作者所有。商業(yè)轉(zhuǎn)載請(qǐng)聯(lián)系作者獲得授權(quán),非商業(yè)轉(zhuǎn)載請(qǐng)注明出處。