要打開一個頁面,總會牽扯到很多資源惕医,其中包括但不局限于以下這些內(nèi)容:
頁面本身,頁面包含的自頁面(比如通過frame噩斟,iframe曹锨,等等);
CSS與JS等腳本(如果你安裝了Mathematica剃允,那么你還可以調(diào)用這貨的網(wǎng)頁腳本來執(zhí)行各種命令沛简。此外還有古老的VBS甚至WSHost的Shell);
各種圖片等資源文件斥废;
其它(這句好廢哦……)
因此椒楣,打開一個頁面,不單單是打開一個頁面牡肉,而是需要再如很多東西捧灰。
下面就來說以下關(guān)于頁面緩存的事。
頁面緩存是干嘛的?
說到這個毛俏,就要先說一個瀏覽器的基本機(jī)制炭庙。
以Webkit為例(我也只熟Webkit。煌寇。焕蹄。)
Webkit有一個Resource模塊,用來管理各種資源的加載與使用——如果你有幸看過圖形圖像引擎gdx的話阀溶,你會發(fā)現(xiàn)它也有一個Resource模塊腻脏,干的是相同的事情,不過針對的是OpenGL/OpenGLES罷了(所以還會緩存shader文件)银锻。
這個模塊的基本邏輯是這樣的:
你問我要一個資源永品,如果沒有,就按照URL去請求击纬;如果有鼎姐,就詢問是否過期(內(nèi)存中是否過期以及資源本身是否過期),如果過期掉弛,就按照URL請求症见,如果沒過期,就直接返回這個對象殃饿。
因為有這個模塊谋作,所以如果你在一個網(wǎng)頁里同時需要現(xiàn)實多張一樣的圖片,它們不會被從服務(wù)器請求多次乎芳,而是只請求一次遵蚜,然后所有需要顯示圖片的Webview組件同時加載這同一份資源。
所以奈惑,在網(wǎng)速較慢的時候吭净,你會發(fā)現(xiàn)這多張圖片是被同時從上到下刷新的;如果加載的是GIF肴甸,那么這些GIF也是按照同一個起點(diǎn)在播放的(這里牽扯到了一些和EventLoop以及GIF的GetFrame相關(guān)的機(jī)制寂殉,所以略有不同,這里不說了)原在。
這里重要的就是:瀏覽器對資源是有做緩存的友扰,只不過會檢測資源是否過期,僅此而已庶柿。
嚴(yán)格說來村怪,上面的說法還不完全對。
比如浮庐,如果是內(nèi)存中資源過期甚负,那么瀏覽器并不是直接從URL請求,而是會先從本地緩存讀取,同時判斷本地緩存是否過期——這就是所謂的資源本身是否過期梭域。
也就是說斑举,如果一份資源在本地緩存中存在,且沒有過期病涨,那么是不會從URL指定位置重新要數(shù)據(jù)的懂昂。
這就是資源的Head中的Expire的作用——它告訴瀏覽器這份資源多久過期。當(dāng)然没宾,也可以通過發(fā)別的Head讓資源立刻過期。
老式互聯(lián)網(wǎng)時代的老式網(wǎng)站里經(jīng)常會看到很多資源的Expire很長很長沸柔,于是你會郁悶地發(fā)現(xiàn)訪問一個網(wǎng)站結(jié)果看到的還是老的圖片循衰,這個很郁悶。老式網(wǎng)站的一般解決方案是:把資源換個名字褐澎。会钝。。工三。迁酸。。瀏覽器里會保留老的資源直到過期俭正,同時還有一份新的資源奸鬓,所以IE的本地緩存往往十分巨大,Windows優(yōu)化大師的一般瘦身攻略就是干掉IE緩存掸读。串远。。
到這里儿惫,我們已經(jīng)接觸到了第一類頁面緩存機(jī)制:Cache-Expire機(jī)制澡罚。
現(xiàn)代瀏覽器當(dāng)然不僅僅有這種古老的機(jī)制,還有很多別的方案肾请。
比如留搔,HTML5中可以用來緩存的方案就很多,比如:
Application Cache铛铁,LocalStorage隔显,F(xiàn)ileSystem,WebDB避归,IndexDB荣月,等等。
Application Cache是一個很牛叉的東西梳毙,它通過一份配置文檔告訴瀏覽器哪些資源是直接保存在本地的哺窄,這個意思就是說:這份資源就在你的電腦上,訪問的時候也是直接從你電腦上訪問,而不需要在問瀏覽器萌业。
和傳統(tǒng)的Cache-Expire機(jī)制的不同就在于檢測是否過期這件事上坷襟。
如果資源的過期時間很長,那么一旦網(wǎng)站有更新生年,你就無法即使更新圖片婴程,會有問題。而且這種機(jī)制對頁面無效抱婉。另一方面档叔,如果Expire很短,那么緩存十張圖片蒸绩,那么在你再次打開頁面的時候衙四,會發(fā)送十次資源請求,服務(wù)器回答沒有更新可以拿老的資源(304信號)患亿,然后你才可以繼續(xù)拿老的最遠(yuǎn)而不是請求新的資源传蹈,換言之存在一個詢問是否過期的過程,十張圖片就是十次步藕,不是很節(jié)約惦界。尤其網(wǎng)絡(luò)環(huán)境不好的時候,就會導(dǎo)致加載速度變慢咙冗。
Application Cache就不同了沾歪,一個頁面的所有資源是否過期完全由配置文檔是否過期來決定,如果配置文檔沒有過期乞娄,那么整個頁面的資源都不需要重拿瞬逊;如果配置文檔過期,那么再按照正常的Expire-Cache流程走——換言之仪或,在不需要更新的時候确镊,Application Cache只需要確認(rèn)一次過期,而傳統(tǒng)的是有多少資源確認(rèn)多少自范删,浪費(fèi)Http鏈接蕾域;而在緩存已經(jīng)過期的時候,則和傳統(tǒng)方案一致到旦。
更重要的是旨巷,Application Cache由于上述機(jī)制真正做到了離線瀏覽——雖然傳統(tǒng)的老的瀏覽器早就只是了離線模式,但還是需要用戶自己來開啟添忘,Application Cache則是全自動的采呐。
但Application Cache有一個問題,拿就是它是以文件為單位的搁骑,而且是由服務(wù)器決定的斧吐。如果是一些運(yùn)行到一般的中間變量又固,那AC就無效了。
這個時候你需要LocalStorage煤率。
LocalStorage將數(shù)據(jù)以Key-Value這種橙須猿很熟悉的形式保存在瀏覽器的本地緩存中仰冠,從而不會因為網(wǎng)頁的關(guān)閉而消失。
而且蝶糯,它不是以文件為單位洋只,而是以變量為單位,這就很靈活了昼捍。
當(dāng)我們作一個大型網(wǎng)站识虚,或者一個WebApp(比如游戲)的時候,就會大量使用LocalStorage來保存一些用戶設(shè)定或者游戲進(jìn)度等信息妒茬。
基本上舷礼,如果一份信息不需要和別人交互,服務(wù)器也并不真的非要不可郊闯,那么就可以保存在本地——這就是LocalStorage的功能。
比如說蛛株,簡書的黑夜/白天設(shè)置团赁,這個其實完全可以保存在LocalStorage中——但如果用戶都要求說我從不同的設(shè)備登錄這個網(wǎng)站都要拿到相同的設(shè)定,拿就還是需要保存在服務(wù)器的(當(dāng)然谨履,就個人來看欢摄,這種要求可以忽略……)。
使用LocalStorage可以將這些無關(guān)的設(shè)定保存在客戶端笋粟,從而節(jié)約服務(wù)器端數(shù)據(jù)庫空間(雖然估計很有限)怀挠,需要的時候從客戶端拿嘛。
LocalStorage的胞弟SessionStorage也可以用來保存數(shù)據(jù)害捕,不過這種數(shù)據(jù)一般就是單窗口有效的绿淋。SessionStorage的數(shù)據(jù)只在打開頁面的窗體內(nèi)始終保存,但和頁面級的保存不同尝盼,你跳轉(zhuǎn)頁面后這些數(shù)據(jù)還在吞滞,直到你關(guān)閉這個窗口位置。
LocalStorage還提供update事件盾沫,從而可以做到一個網(wǎng)站的多個窗口之間的數(shù)據(jù)同步和通訊裁赠,這個對IM或者游戲來說很重要。
LocalStorage的一個限制是赴精,不同的瀏覽器有不同的空間上限佩捞,比如Chrome是5MB,一般來說也是夠用了蕾哟。
比如LocalStorage更專業(yè)的就是WebDB了一忱,以數(shù)據(jù)庫的格式在本地維護(hù)數(shù)據(jù)莲蜘,做游戲必備,這里不多解釋掀潮。
Local Storage用來保存零散的變量菇夸,Application Cache則只能以配置好的文件為單位緩存文件,不夠靈活仪吧。
這種的方案庄新,就是超級重量級的大殺器:FileSystem。
FileSystem讓網(wǎng)站設(shè)計者有權(quán)限在瀏覽器于用戶本地的可用空間范圍任意讀寫文件薯鼠。
這就非常靈活了择诈,不受Application Cache的配置限制,也不像Local Storage有格式和容量限制出皇。
這貨要怎么用羞芍,就完全看網(wǎng)站設(shè)計者高興了。
下面來說一下比較常見的綜合型緩存機(jī)制郊艘。
首先荷科,傳統(tǒng)的Expire-Cache機(jī)制肯定還在。
然后纱注,就是網(wǎng)站的最穩(wěn)定的部分畏浆,比如HTML框架,核心JS與CSS狞贱,這些都用ApplicationCache做緩存刻获,基本都是半年一年甚至再也不會更改的,很穩(wěn)定瞎嬉。
社交類網(wǎng)站除了上述框架外蝎毡,最經(jīng)常變動的,就是文章帖子內(nèi)容氧枣,以及沐兵,各種圖片聲音等資源。
上述資源又可以分為幾類——
有些是“一次性消費(fèi)”便监,比如今天看了一篇文章痒筒,上面出現(xiàn)了某個用戶的頭像,但以后我再也不看了茬贵。
所有訪問次數(shù)很少的(基本三次一下就可以認(rèn)為很少)簿透,都屬于一次性消費(fèi),這類東西沒必要做緩存解藻,不值當(dāng)老充。
有些是“長期消費(fèi)”,比如我關(guān)注的用戶中最活躍的幾個螟左,他們的文章我撤茸牵看觅够,于是它們的頭像對我來說就是長期消費(fèi)——不但是頭像這個圖片資源,也包括它們的個人數(shù)據(jù)中穩(wěn)定不變的部分巷嚣,比如個人簡介喘先。這種資源一個兩個人也就算了,人數(shù)一多廷粒,也就很可觀了窘拯。還比如某些類型的網(wǎng)站會出現(xiàn)一些文章一個用戶會長期經(jīng)常反復(fù)訪問(比如李毅吧置頂帖)。
還有一些是“穩(wěn)定消費(fèi)”坝茎,比如一個論壇的主題(包括一組CSS涤姊、JS和圖片),基本我選定以后一長段時間里不會變嗤放。它們尷尬的地方是——相比框架性資源思喊,它們沒那么恒常;相對長期消費(fèi)資源次酌,它們又足夠穩(wěn)定恨课。所以這類東西的緩存就要使用不同的方案。
對于穩(wěn)定消費(fèi)資源岳服,就可以使用FileSystem寫到本地庄呈,當(dāng)用戶換主題的時候就刪除久主題,重寫新主題派阱。一套主題資源寫到FS里,以后的開銷都問FS拿斜纪,不需要再問服務(wù)器要贫母,節(jié)約了流量和加載時間。
而長期消費(fèi)中非圖片聲音等大型資源外盒刚,內(nèi)容數(shù)據(jù)就可以用LocalStorage來做緩存處理——就算只有5MB對純文本來說也足夠了腺劣,當(dāng)然你別跟我提什么你叔叔純TXT的黃書有10GB這種奇葩案例……長期消費(fèi)中的大型資源可以考慮用FileSystem做緩存。
當(dāng)然因块,對于JS文件和CSS文件橘原,可以使用FS緩存,也可以寫入LS中(使用的是偶從LS中讀取然后寫入Script或者Style標(biāo)簽動態(tài)加載到頁面中即可)涡上。
用戶個性化設(shè)置也可以用LocalStorage趾断,最多就是每次有修改了告訴服務(wù)器,第一次瀏覽的時候拿一遍吩愧,而不需要每次都從服務(wù)器端拿一遍芋酌。
一次性消費(fèi),無視掉吧雁佳。
因此脐帝,我們完全可以創(chuàng)建一個Resource的JS模塊來完成這件事同云。
它的作用就是根據(jù)請求資源ID來查看LS或者FS,如果有就返回本地緩存數(shù)據(jù)(如果是圖片的URL就返回FS的URL)堵腹,如果沒有再問服務(wù)器要炸站。然后可以開一個服務(wù)器接口是詢問最新資源的TimeStamp,供瀏覽器去確認(rèn)疚顷。
Resource模塊的另一個作用旱易,就是根據(jù)對資源的訪問頻次來決定什么資源使用什么類型的緩存,這是一個純配置和計數(shù)荡含,就不說了咒唆。
當(dāng)然,上面是基本思路释液,實際構(gòu)建模塊的時候肯定還會有別的問題全释,這里就姑且忽略掉吧~~~(這句話好不負(fù)責(zé)任啊…………)