緩存對(duì)于一個(gè)網(wǎng)站來(lái)說(shuō)非常重要,可以提高網(wǎng)站性能,減少冗余的數(shù)據(jù)傳輸,增加服務(wù)器負(fù)擔(dān),web存儲(chǔ)則給瀏覽器提供了更加強(qiáng)大的保存文件的接口。
有相當(dāng)一段時(shí)間一直混淆了HTTP緩存相關(guān)的屬性,HTML5離線存儲(chǔ)和本地儲(chǔ)存的一些關(guān)系,最近好好地整理了一下這些Web存儲(chǔ)相關(guān)的東西
先列出一些相關(guān)屬性和概念,看看能否理清它們之間的區(qū)別和聯(lián)系?
- manifest
- cache-control
- expires
- 304(no modified)
- ETag
- If-None-Match
- Last-Modified
- If-Modified-Since
- http-equiv
- webstorage
- cookie/session
是不是感覺(jué)有點(diǎn)凌亂,那就跟著我整理的筆記走一遍吧:)
首先說(shuō)一說(shuō)HTTP緩存相關(guān)的東西:
Cache-Control:
每一個(gè)用HTTP請(qǐng)求的資源都可以在響應(yīng)頭用Cache-Control來(lái)給瀏覽器定義緩存策略,通過(guò)設(shè)置一些屬性值它可以控制誰(shuí)可以,在什么條件下可以緩存響應(yīng),還有緩存的有效期,這個(gè)屬性的一些常用值如下:
no-cache:
表示不使用緩存,先和服務(wù)器確認(rèn)要返回的資源是否有修改
no-store:
表示禁止瀏覽器和所有中繼緩存響應(yīng)的資源
max-age=100:
表示緩存的有效期,單位是秒,這一段時(shí)間內(nèi),除非緩存文件發(fā)生一些變動(dòng),否則會(huì)直接使用之前的緩存,注意這段時(shí)間內(nèi)是不會(huì)發(fā)Etag等方法去驗(yàn)證的資源有沒(méi)有修改的。緩存的文件發(fā)生變動(dòng),主要有這些情況:資源名更改,資源地址更改,緩存被刪除,網(wǎng)頁(yè)強(qiáng)制刷新等;
資源名更改,給文件名添加版本hash值,比如給image.png修改為image-hash.png,可以保證每次更新文件時(shí)用戶可以重新發(fā)出請(qǐng)求,獲取最新的資源。資源路徑更改,修改文件的請(qǐng)求路徑,比如給image.png添加查詢參數(shù)修改為image.png?hash,max-age的時(shí)間設(shè)置根據(jù)每個(gè)網(wǎng)站的實(shí)際情況不同去設(shè)置,一種極端的做法是把這個(gè)值設(shè)置很大,然后通過(guò)修改資源名或者給資源請(qǐng)求地址添加查詢參數(shù),來(lái)告訴瀏覽器該更新資源了,一般用在很久才更新網(wǎng)站的情況.
public:
用max-age即是默認(rèn)public了,不用設(shè)置
private:
私人緩存,中繼緩存不被允許,但是可以在瀏覽器緩存
HTTP緩存的其他概念:
expires:
表示存在的時(shí)間,使客戶端在這個(gè)設(shè)置的時(shí)間之前不用去請(qǐng)求資源,類似于max-age,但是expires表示的是一個(gè)固定時(shí)間,而且可能有服務(wù)器和客戶端時(shí)間不一致的問(wèn)題,主要用于HTTP1.0版本,在HTTP1.1版本完全可以用功能更強(qiáng)的Cache-Control來(lái)替代,和max-age同時(shí)存在時(shí)expiers會(huì)被覆蓋掉
http-equiv:
緩存有兩種控制機(jī)制,一種是請(qǐng)求頭信息控制,另外一種就是利用meta標(biāo)簽;可以在HTML文檔中為meta標(biāo)簽設(shè)置http-equiv為相應(yīng)屬性名,content為值來(lái)設(shè)置緩存,例如
<meta http-equiv="Expires" content="Mon, 20 Jul 2009 23:00:00 GMT" />
不過(guò)只對(duì)改網(wǎng)頁(yè)的HTML文件有緩存作用,對(duì)該頁(yè)面的其他資源以及其他頁(yè)面的HTML文件都沒(méi)有作用
那么max-age(expires)到期之后,在no-cache下的資源會(huì)先和服務(wù)器確認(rèn)返回的資源是否有修改,如何實(shí)現(xiàn)這一過(guò)程?這就要用到
ETag/If-None-Match,Last-Modified/If-Modified-Since
ETag (Entity Tag)其實(shí)就是一個(gè)驗(yàn)證令牌,用來(lái)標(biāo)識(shí)一個(gè)資源,可能是一個(gè)hash值,也可能是一個(gè)版本號(hào),每當(dāng)資源有修改的時(shí)候ETag的值就會(huì)改變?yōu)g覽器第一次請(qǐng)求之后會(huì)保存響應(yīng)頭的ETag值,以便下一次發(fā)送請(qǐng)求的時(shí)候校驗(yàn)Etag是否有更改。
那么下一次瀏覽器如何告訴服務(wù)器本地已經(jīng)存有Etag和相應(yīng)的資源了呢?
If-None-Match
通過(guò)在請(qǐng)求頭添加If-None-Match(如果存在ETag,瀏覽器會(huì)自動(dòng)添加),賦值為上一次請(qǐng)求后在本地存儲(chǔ)的Etag值,服務(wù)器會(huì)和服務(wù)端最新資源的Etag比對(duì),如果沒(méi)有更改會(huì)直接返回304 no modified給瀏覽器,瀏覽器就直接使用本地緩存的文件
Last-Modified/If-Modified-Since
其作用等同于ETag/If-None-Match,
不過(guò)前者是通過(guò)規(guī)定一個(gè)時(shí)間來(lái)比對(duì),最小的單位是秒,后者通過(guò)一個(gè)唯一標(biāo)識(shí)符,所以可以看出來(lái)如果原站在一秒內(nèi)有多次更新,那么前者就不頂用啦。
ETag的驗(yàn)證要優(yōu)先于Last-Modified,此外ETag也是有缺點(diǎn)的,在分布式的環(huán)境中,Etag在不同服務(wù)器上的同步問(wèn)題可能會(huì)給服務(wù)器帶來(lái)一些壓力。
HTTP緩存是和每一個(gè)HTTP請(qǐng)求直接相關(guān)的,每一個(gè)請(qǐng)求資源的響應(yīng)都有相應(yīng)的緩存策略,它們往往是相似的,是否可以通過(guò)其他的機(jī)制,直接告訴瀏覽器去緩存哪一些文件呢?
HTML5離線存儲(chǔ)閃亮出場(chǎng)
H5離線存儲(chǔ):服務(wù)器通過(guò)一份.manifest文件給瀏覽器提供一份完備的緩存名單,名單包括需要緩存的文件,不需要緩存的文件的列表之外,還有一些其他的功能,比如給資源設(shè)置備選的請(qǐng)求地址,設(shè)置404頁(yè)面等
實(shí)現(xiàn):利用H5的標(biāo)簽新屬性manifest,只需要在HTML文件添加
<html manifest="test.manifest">,
服務(wù)器則將manifest文件的mime-type設(shè)置為text/cache-manifest類型即可,瀏覽器每次請(qǐng)求會(huì)檢查manifest文件是否有更新,服務(wù)端通過(guò)修改manifest文件,即可在瀏覽器下一次請(qǐng)求資源的時(shí)候通知其更新相應(yīng)的資源。
作用:通過(guò)本地離線存儲(chǔ)可以在沒(méi)有網(wǎng)絡(luò)的情況下訪問(wèn)網(wǎng)站事先保存的文件資源,在有網(wǎng)絡(luò)的情況下直接使用本地資源也可以減少請(qǐng)求連接的壓力,提高網(wǎng)頁(yè)的加載速度,注意這里和HTTP緩存的區(qū)別,它是直接使用本地資源,請(qǐng)求返回的是200(from cache),由于有manifest來(lái)統(tǒng)一管理,所以不需要發(fā)請(qǐng)求查看是否有更新,也沒(méi)有過(guò)期時(shí)間。
PS:這里關(guān)于manifest文件自身的更新問(wèn)題,還是要走HTTP緩存,或者直接不緩存這個(gè)文件。
前面提到的概念主要都是緩存請(qǐng)求文件這一塊的東西,它們的目的都是為了提高網(wǎng)頁(yè)的性能,可以說(shuō)是一種優(yōu)化型的存儲(chǔ),可以給用戶帶來(lái)更流暢的體驗(yàn)
但是我們印象中還有一種存儲(chǔ),它可以給我們提供更多的可能和效果的實(shí)現(xiàn),是功能型的存儲(chǔ),如H5本地儲(chǔ)存Webstorage,cookie,session
cookie,session
眾所周知,HTTP是無(wú)狀態(tài)的協(xié)議,每一次請(qǐng)求都是獨(dú)立沒(méi)有聯(lián)系的,瀏覽器和服務(wù)器都沒(méi)有辦法維持用戶的狀態(tài),判斷用戶是不是依然是之前的那個(gè)用戶。
很容易可以想到,在同一個(gè)用戶的每個(gè)請(qǐng)求頭添加一個(gè)唯一標(biāo)識(shí)符,通過(guò)判斷這個(gè)標(biāo)識(shí)符就可以維持用戶的一些信息和狀態(tài)。
會(huì)話信息被用來(lái)作為標(biāo)識(shí)符解決這個(gè)問(wèn)題,cookie機(jī)制采用的是在客戶端保持狀態(tài)的方案,而session機(jī)制采用的是在服務(wù)器端保持狀態(tài)的方案(服務(wù)端保持狀態(tài)也需要客戶端保持狀態(tài),所以一般session都要基礎(chǔ)cookie或者sessionstorage)
總的來(lái)說(shuō):cookie數(shù)據(jù)放在客戶端,session數(shù)據(jù)放在服務(wù)端,cookie可以設(shè)置期限,session則是關(guān)閉瀏覽器時(shí)銷毀(cookie默認(rèn)也是),cookie不安全,session可能會(huì)影響服務(wù)器性能
Cookie:通常用Javascript封裝一個(gè)setCookie的函數(shù)來(lái)創(chuàng)建,有大小限制,可能會(huì)導(dǎo)致請(qǐng)求頭過(guò)于臃腫,瀏覽器發(fā)送請(qǐng)求時(shí),檢查本地cookie,如果該cookie聲明的范圍大于發(fā)送請(qǐng)求的url地址時(shí),就會(huì)自動(dòng)在請(qǐng)求上添加cookie字段
session:服務(wù)器每次會(huì)檢查瀏覽器請(qǐng)求頭的session標(biāo)識(shí),如果有則將這個(gè)session id在服務(wù)器的數(shù)據(jù)庫(kù)(散列表)查找,找到后才進(jìn)行相應(yīng)權(quán)限的操作
如果沒(méi)有這個(gè)請(qǐng)求標(biāo)識(shí),則為客戶端新建一個(gè),返回給客戶端后,客戶端可以通過(guò)cookie或者sessionstorage來(lái)保存這個(gè)session id,通過(guò)請(qǐng)求頭的cookie字段來(lái)給服務(wù)器提供sessionId,當(dāng)cookie被禁止時(shí)就需要一些其他方法,通常是采用添加到url路徑中,添加表單隱藏域等方法
cookie作為HTTP協(xié)議規(guī)范的一部分,主要還是用來(lái)儲(chǔ)存用戶信息的,用來(lái)和服務(wù)端交互,大小也有限制,僅僅只是一種會(huì)話級(jí)別的存儲(chǔ)
localstorage,sessionstorage
webstorage為了更大的存儲(chǔ)文件設(shè)計(jì),與Cookie負(fù)責(zé)記錄用戶信息相比,webStorage專注于本地存儲(chǔ),通過(guò)封裝好的setItem,getItem等方法即可使用
localstorage是一種持久化的存儲(chǔ),除非主動(dòng)刪除,否則永遠(yuǎn)存在,sessionstorage主要用來(lái)存儲(chǔ)會(huì)話(session),關(guān)閉瀏覽器就會(huì)銷毀,是一種非持久化的存儲(chǔ)
除了上面這些的概念之外,還有諸如像IndexDB,F(xiàn)ileSystem等存儲(chǔ)方法,關(guān)于Web存儲(chǔ)的知識(shí)真的非常多,看上去坑也是不少,還需要實(shí)踐慢慢來(lái)掌握。