現(xiàn)代的瀏覽器以及服務(wù)器都支持壓縮技術(shù)哥放,唯一需要協(xié)商的是采用的壓縮算法歼指。 為了選擇采用的壓縮算法,瀏覽器和服務(wù)器之間會使用?主動協(xié)商機(jī)制
瀏覽器發(fā)送?Accept-Encoding?首部婶芭,(其中包含它所支持的壓縮算法东臀,以及各自優(yōu)先級)
服務(wù)器則從中選擇一種,使用該算法對響應(yīng)消息主體進(jìn)行壓縮犀农,并且發(fā)送?Content-Encoding首部來告知瀏覽器它使用了哪一種算法
由于該內(nèi)容協(xié)商過程是基于編碼類型來選擇資源的展現(xiàn)形式的惰赋,在響應(yīng)中,?Vary?(渲染引擎)首部中至少要包含?Accept-Encoding?;這樣,緩存服務(wù)器就可以對資源的不同的展現(xiàn)形式進(jìn)行緩存赁濒。
如果有想學(xué)習(xí)編程的初學(xué)者轨奄,可來我們的前端直播授課群的哦:733395506里面免費(fèi)送整套系統(tǒng)的前端教程!
如下圖:
也就是:
客戶端(HTTP請求頭) -------> accept-encoding: gzip,deflate,br,sdch
服務(wù)器(HTTP響應(yīng)頭) -------> content-encoding:gzip
實(shí)例
gzip(filePath, req,res, statObj) {letencoding = req.headers["accept-encoding"]if(encoding) {if(encoding.match(/gzip/)) {res.setHeader("Content-Encoding","gzip")returnzlib.createGzip()? ? ? ? ? ? }elseif(encoding.match(/deflate/)) {res.setHeader("Content-Encoding","deflate")returnzlib.createDeflate()? ? ? ? ? ? }returnfalse? ? ? ? }returnfalse? ? }letflag = this.gzip(filePath, req,res, statObj)lettype= mime.getType(filePath) ||"text/plain"res.setHeader("Content-Type",type+"; charset=utf8")if(!flag){? ? ? ? fs.createReadStream(filePath).pipe(res)? ? }else{? ? ? ? fs.createReadStream(filePath).pipe(flag).pipe(res)? ? }復(fù)制代碼
壓縮的優(yōu)缺點(diǎn)
優(yōu)點(diǎn):減少HTTP響應(yīng)時間拒炎,提升傳輸效率
壓縮過程占用服務(wù)器額外的CPU周期挪拟,客戶端也要對壓縮文件進(jìn)行解壓縮,這也需要占用部分時間击你。
總結(jié):
請求頭:
Accept-Encoding: gzip,deflate,br,sdch玉组;告知服務(wù)器自己支持的壓縮格式
user-agent: 不同設(shè)備自動帶上這個頭,可以判斷什么樣的設(shè)備丁侄,重定向到相同的項(xiàng)目惯雳,實(shí)現(xiàn)不同設(shè)備響應(yīng)不同項(xiàng)目
響應(yīng)頭:
COntent-encoding:gzip; 告知瀏覽器,服務(wù)器使用的壓縮格式
Content-Type: 服務(wù)器給瀏覽器響應(yīng)內(nèi)容的類型
Location: 重定向到某個地方
2.緩存
假設(shè)瀏覽器存在一個緩存數(shù)據(jù)庫鸿摇,用于存儲緩存信息石景。
在客戶端第一次請求數(shù)據(jù)時,此時緩存數(shù)據(jù)庫中沒用對應(yīng)的緩存數(shù)據(jù)拙吉,需要請求服務(wù)器潮孽,服務(wù)器返回后,將數(shù)據(jù)存儲至緩存數(shù)據(jù)庫中筷黔。
HTTP緩存有多種規(guī)則往史,根據(jù)是否需要重新向服務(wù)器發(fā)起請求進(jìn)行分類 將其分為兩大類(**強(qiáng)制緩存,對比緩存又叫協(xié)商緩存)
1. 已存在緩存數(shù)據(jù)必逆,僅基于強(qiáng)制緩存怠堪,請求數(shù)據(jù)如下
2. 已存在緩存數(shù)據(jù),僅基于對比緩存名眉,請求數(shù)據(jù)如下
兩類緩存規(guī)則不同:
強(qiáng)制緩存如果生效粟矿,不需要再和服務(wù)器發(fā)生交互
對比緩存(協(xié)商緩存)不管是否生效,都需要與服務(wù)器端發(fā)生交互
**兩類緩存規(guī)則同時存在時损拢,強(qiáng)制緩存優(yōu)先級高于對比緩存陌粹,也就是說,當(dāng)執(zhí)行強(qiáng)制緩存的規(guī)則時福压,如果緩存生效掏秩,直接使用緩存,不再執(zhí)行對比緩存規(guī)則
強(qiáng)制緩存
在沒有緩存數(shù)據(jù)時荆姆,瀏覽器向服務(wù)器請求數(shù)據(jù)時蒙幻,服務(wù)器會將數(shù)據(jù)和緩存規(guī)則一并返回,?緩存規(guī)則信息包含在響應(yīng)header中?胆筒;
對于強(qiáng)制緩存來說邮破,響應(yīng)頭中會有連個字段表名失效規(guī)則(?Expires/Cache-Control?)
Expires
Expries的值為服務(wù)器端返回的到期時間诈豌,即下一次請求時,請求的時間小于服務(wù)端返回到期時間抒和,直接使用緩存數(shù)據(jù)矫渔。不過Expries是HTTP1.0的東西,現(xiàn)在瀏覽器默認(rèn)使用的是HTTP1.1摧莽,所以它的作用基本忽略
另一個問題庙洼,到期時間使用服務(wù)端生成的,但是客戶端時間可能跟服務(wù)端時間有誤差镊辕,這就導(dǎo)致了緩存命中的誤差油够,因此,HTTP1.1版本中征懈,使用了Cache-Control替代
Cache-Control
Cache-Control是最重要的規(guī)則叠聋,其常見取值:
private:客戶端可以緩存
public:客戶端和代理服務(wù)器都可緩存
max-age=xxx:緩存的內(nèi)容將在xxx秒后失效
no-cache:需要使用對比緩存來驗(yàn)證緩存數(shù)據(jù)
no-store:所有內(nèi)容都不會緩存,?強(qiáng)制緩存受裹,對比緩存都不觸發(fā)?,
實(shí)例:
圖中Cache-Control僅指定了max-age虏束,所有默認(rèn)是private棉饶,緩存時間是31536000秒(365天) 也就是說,在365天內(nèi)再次請求這條數(shù)據(jù)镇匀,都會直接獲取緩存數(shù)據(jù)庫中的數(shù)據(jù)照藻,直接使用。
對比緩存(協(xié)商緩存)
瀏覽器第一次請求數(shù)據(jù)時汗侵,服務(wù)器會將緩存標(biāo)識u數(shù)據(jù)一起返回給客戶端幸缕,客戶端將二者備份至緩存數(shù)據(jù)庫中, 再次請求數(shù)據(jù)時晰韵,客戶端將備份的數(shù)據(jù)標(biāo)識發(fā)送給服務(wù)器发乔,服務(wù)器根據(jù)緩存標(biāo)識進(jìn)行判斷,判斷成功后雪猪,返回304狀態(tài)碼栏尚,通知客戶端比較成功,可以使用緩存數(shù)據(jù)
通過兩圖對比只恨,可發(fā)現(xiàn)译仗,在對比緩存生效時,狀態(tài)碼是304官觅,并且報(bào)文大小和請求時間大大減少纵菌。 原因是,服務(wù)器在進(jìn)行標(biāo)識比較后休涤,只返回header部分咱圆,通過狀態(tài)碼通知客戶端使用緩存,不再需要將報(bào)文主體部分返回給客戶端。
緩存標(biāo)識:
Last-Modified/If-Modified-Since
Last-Modified:
服務(wù)器在響應(yīng)請求時闷堡,告訴瀏覽器資源的最后修改時間隘膘。
If-Modified-Since:
再次請求服務(wù)器時,通過此字段通知服務(wù)器上次請求時杠览,服務(wù)器返回的資源最后的修改時間弯菊。 服務(wù)器收到請求后發(fā)現(xiàn)頭有 If-Modified-Since 則與請求資源的最后修改時間進(jìn)行對比。 若資源的最后修改時間大于 If-Modified-since 踱阿,說明資源又被改動過管钳,則響應(yīng)整片資源內(nèi)容,返回狀態(tài)碼200软舌; 若資源的最后修改時間小于或等于 If-Modified-Since 才漆,說明資源沒有新修改過,則響應(yīng)HTTP304佛点,告訴繼續(xù)使用緩存中的數(shù)據(jù)
**Etag/If-None-Match
優(yōu)先級高于 Last-Modified/If-Modified-Since
Etag:
服務(wù)器響應(yīng)請求時醇滥,告訴瀏覽器當(dāng)前資源在服務(wù)器的唯一標(biāo)識(摘要)
If-None-Match:
再次請求服務(wù)器時,通過此字段通知服務(wù)器客戶端緩存數(shù)據(jù)的唯一標(biāo)識超营, 服務(wù)器收到請求后發(fā)現(xiàn)請求頭中有 If-None-Match 則與被請求資源的唯一標(biāo)識進(jìn)行比對鸳玩, 不同,說明資源又被改動過演闭,則響應(yīng)整片資源內(nèi)容不跟,返回狀態(tài)碼200; 相同米碰,說明資源沒有新修改過窝革,則響應(yīng)HTTP304,告知瀏覽器使用緩存數(shù)據(jù)
總結(jié)
對于強(qiáng)制緩存吕座,服務(wù)器通知瀏覽器一個緩存時間虐译,在緩存時間內(nèi),下次請求米诉,直接用緩存菱蔬,不在時間內(nèi),執(zhí)行比較緩存策略:
Expries/Cache-Control:max-age=xxx > Etag/If-None-Match > Last-Modified/If-Modified-Since
對于比較緩存史侣,將緩存信息中的Etage和Last-Modified通過請求發(fā)送給服務(wù)器拴泌,由服務(wù)器校驗(yàn),返回304狀態(tài)碼時惊橱,瀏覽器直接使用緩存數(shù)據(jù)蚪腐。
瀏覽器第一次請求:
瀏覽器再次請求時:
實(shí)例:
cache(filePath, req, res,statObj){letlastModified =statObj.ctime.toGMTString()letifModifiedSince = req.headers['if-modified-since']letEtag =? ? ? ? ? ? crypto.createHash("md5").update(fs.readFileSync(filePath)).digest("base64")? ? ? ? res.setHeader("Last-Modified",lastModified)? ? ? ? // Etag是響應(yīng)頭? ? ? ? res.setHeader("Etag",Etag)? ? ? ? //if-none-match 當(dāng)你修改服務(wù)器上的文件時,請求頭上面會自動添加這個頭? ? ? ? // console.log(req.headers['if-none-match'],"match")? ? ? ? // console.log(Etag)? ? ? ? //if-none-match: NISthsES8P9vzWjdFT/xyg== match? ? ? ? // console.log(req.headers['if-none-match'])? ? ? ? // T9hRJPsOY4/I9QhWp+NFlQ==? ? ? ? // 如果if-none-match存在税朴,說明你改動服務(wù)器上的文件中的內(nèi)容? ? ? ? // T9hRJPsOY4/I9QhWp+NFlQ==? ? ? ? // console.log("Etag--->",Etag)? ? ? ? // console.log("if-none-match--->",req.headers['if-none-match'])letifNoneMatch = req.headers['if-none-match']? ? ? ? // // 根據(jù)內(nèi)容摘要判斷是否需要緩存? ? ? ? //if(ifNoneMatch){? ? ? ? //? ? //ifNoneMatch說明修改了內(nèi)容? ? ? ? //? ? //returnfalse;? ? ? ? //if(ifNoneMatch !== Etag){? ? ? ? //? ? ? ? // 修改了內(nèi)容回季,并且沒恢復(fù)家制,走網(wǎng)絡(luò)? ? ? ? //returnfalse;? // 不走緩存? ? ? ? //? ? }else{? ? ? ? //? ? ? ? // 修改了內(nèi)容,并且修改完后泡一,把內(nèi)容恢復(fù)颤殴,相當(dāng)于沒有修改? ? ? ? //returntrue; // 還是從緩存中取數(shù)據(jù)? ? ? ? //? ? }? ? ? ? // }else{? ? ? ? //? ? // 說明內(nèi)容沒有改變? ? ? ? //returntrue//? ? ? ? // }? ? ? ? // // 根據(jù)修改時間來判斷是否緩存? ? ? ? //if(ifModifiedSince){? ? ? ? //if(ifModifiedSince !== lastModified){? ? ? ? //? ? ? ? // 上一次修改的時間和最新修改的時間不一樣? ? ? ? //returnfalse//? 不走緩存? ? ? ? //? ? }? ? ? ? // }? ? ? ? // 壓縮和緩存是后端程序干的if(ifModifiedSince &&ifNoneMatch){if(ifNoneMatch !== Etag &&ifModifiedSince !== lastModified){returnfalse}? ? ? ? }else{returnfalse}returntrue} sendFile(filePath, req, res,statObj) {? ? ? ? res.setHeader("Cache-Control","no-cache");letcache = this.cache(filePath, req, res,statObj)if(cache){? ? ? ? ? ? res.statusCode = 304;returnres.end()? ? ? ? }letflag = this.gzip(filePath, req, res,statObj)lettype= mime.getType(filePath) ||"text/plain"res.setHeader("Content-Type",type+"; charset=utf8")if(!flag){? ? ? ? ? ? fs.createReadStream(filePath).pipe(res)? ? ? ? }else{? ? ? ? ? ? fs.createReadStream(filePath).pipe(flag).pipe(res)? ? ? ? }? ? }復(fù)制代碼