深度理解 圖片預(yù)加載和緩存機制

本文通過兩個圖片預(yù)加載案例引起的緩存相關(guān)問題旺上,探討了圖片預(yù)加載處理技術(shù),和瀏覽器網(wǎng)絡(luò)請求以及緩存機制的一些問題捂人。

2017-04-25 By Herbert Chow

問題起源分析:

一纵揍、騰訊游戲:征服星際 戰(zhàn)出未來 移動視頻h5中(如圖1),采用了預(yù)加載圖片技術(shù)刷允,先有一個loading頁冤留,然后一個視頻,之后是一個落地頁結(jié)束树灶。

在預(yù)加載圖片時纤怒,圖片會有一定的概率發(fā)生重復(fù)請求(重復(fù)加載)的情況并且被重復(fù)加載的圖片會出現(xiàn)閃爍,就號像根本就沒有預(yù)加載一樣天通,原因不明泊窘,如(圖2),而且如果通過自己手寫函數(shù)進行預(yù)加載圖片像寒,會有極大幾率發(fā)生重復(fù)請求的想象烘豹;而該用pxloader.js進行圖片加載的話,幾率大大減少诺祸,但不為100%不出現(xiàn)携悯。

a.png

圖1

a2.png

圖2

二、陰陽師扭蛋預(yù)約h5(如圖3):與上問題一相似筷笨,使用網(wǎng)易內(nèi)部組件trueload組件進行加載憔鬼,在預(yù)加載圖片時,會有一定幾率會重復(fù)加載zhaohuanzhen_的序列幀套圖胃夏,本來只有46張轴或,后面重復(fù)加載到了85張,或者92張(完全重復(fù)請求了一遍)仰禀。流程是照雁,先loading頁,再到登陸頁悼瘾,再到抽獎頁面囊榜,最后是結(jié)果頁面。在抽獎頁面亥宿,會用到canvas調(diào)用loading時緩存的序列幀圖片卸勺。

a3.png

圖3

注:當(dāng)圖片重復(fù)請求時,會造成圖片閃爍烫扼,有可能是因為序列幀動畫切換比較連貫曙求,所以看起來閃爍會比較明顯,而平時hover時看可能不那么明顯映企。本質(zhì)問題應(yīng)該還是圖片重復(fù)請求的問題悟狱。

問題假設(shè):

一、是否只要在頁面中請求一次圖片A并且成功后堰氓,那么圖片A在后續(xù)的設(shè)置img.src或者設(shè)置bgi時挤渐,都不會發(fā)生重復(fù)請求,而是直接用緩存双絮;

二浴麻、是否只要圖片的路徑是一樣的得问,讀取了一次成功后,就不需再次請求软免,直接讀緩存宫纬。

論證猜測(不供證明,僅供參考):

一膏萧、用Pxloader或者trueLoad等組件進行加載時漓骚,會比自己手寫加載函數(shù)(大神請忽略)會好很多,實驗證明榛泛,自己手寫預(yù)加載函數(shù)一般會缺少一些邏輯判斷蝌蹂,導(dǎo)致效果不好,會有較大概率出現(xiàn)重復(fù)請求的情況曹锨。查閱一下Pxloader的源碼叉信,發(fā)覺,簡單的一個圖片加載函數(shù)艘希,代碼量也還是挺大的,不是我們自己一百幾十行就能搞定的硅急。

舉例:有可能出現(xiàn)bug原因是覆享,我們自己寫的加載函數(shù)還沒加載完圖片,然后業(yè)務(wù)邏輯代碼已經(jīng)又進行了一次圖片的請求营袜,這樣就會造成同一張圖同時被請求了兩次撒顿。

成因剖析(第一部分):預(yù)備概念的理解

1、圖片src或者background-image (下面簡稱bgi)設(shè)置和切換問題:

參考鏈接: https://github.com/jieyou/lazyload#%E4%B8%8Esrcset%E5%B1%9E%E6%80%A7%E4%B8%80%E8%B5%B7%E4%BD%BF%E7%94%A8

參考鏈接中提到荚板,“創(chuàng)建的Image對象會加載一次凤壁,實際DOM樹中的元素設(shè)置 src 或 background-image 時又會加載一次”,只要image對象被創(chuàng)建跪另,并且設(shè)置其src屬性時拧抖,瀏覽器就會發(fā)起請求,或者html中dom結(jié)構(gòu)中的圖片img標(biāo)簽src發(fā)生變化免绿,big發(fā)生變化時唧席,也會發(fā)生請求。

2嘲驾、瀏覽器對于圖片請求的流程

參考鏈接:http://er.dadaaierer.com/?p=431

參考鏈接中提到:

關(guān)于圖片緩存

如果圖片已經(jīng)在緩存中:

1)瀏覽器不會發(fā)起請求去請求圖片淌哟,瀏覽器直接從緩存中拿圖片。

2)而設(shè)置過期時間會強迫瀏覽器在訪問頁面的時候去請求圖片辽故,如果圖片已經(jīng)在緩存中徒仓,并且正在被重新請求,瀏覽器會把最后修改時間加入在HTTP頭中誊垢,這就是傳統(tǒng)的GET請求掉弛,如果圖片沒有被修改症见,服務(wù)器會返回一個304代碼,所以對于瀏覽器的請求服務(wù)器會返回下面的兩種代碼:

200–瀏覽器沒有緩存狰晚。

304–瀏覽器已經(jīng)緩存了圖片筒饰,但是需要驗證最后修改時間

也就是說,瀏覽器在需要進行圖片操作之前壁晒,會先查看本地是否有緩存瓷们,如果有,會先讀取緩存秒咐;如果沒有谬晕,才會去發(fā)起網(wǎng)絡(luò)請求。

成因剖析(第二部分):分析原因

一携取、如圖4攒钳,是騰訊外星人h5在一定概率刷新時,發(fā)生重復(fù)請求雷滋,此刻注意到網(wǎng)絡(luò)部分不撑,size圖片請求時間是0ms,而圖片大小其實是沒有標(biāo)明的晤斩,寫著 from menory cachefrom disk cache焕檬,此刻還注意到,網(wǎng)絡(luò)請求時200而不是應(yīng)該是語氣中的304澳泵,十分奇怪实愚。

a4.png

圖4

二、如圖5兔辅,陰陽師扭蛋h5腊敲,有一定概率,在發(fā)起200請求之后預(yù)加載圖片后维苔,發(fā)起重復(fù)請求并且返回狀態(tài)碼304碰辅。

a5.png

圖5

三、這里有幾點要注意

1蕉鸳、200和304跟緩存的聯(lián)系

2乎赴、請求發(fā)起者initiator

3、size(from memory cache和from disk cache)

四潮尝、知識點再次分析

對于上面三點

1榕吼、參考鏈接: https://www.oschina.net/question/1395553_175941

參考文中提到,

其實勉失, 200 OK (from cache) 是瀏覽器沒有跟服務(wù)器確認(rèn)羹蚣,直接用了瀏覽器緩存;而 304 Not Modified 是瀏覽器和服務(wù)器多確認(rèn)了一次緩存有效性乱凿,再用的緩存顽素。200(from cache) 是速度最快的,因為不需要訪問遠程服務(wù)器,直接使用本地緩存.304 的過程是, 先請求服務(wù)器, 然后服務(wù)器告訴我們這個資源沒變, 瀏覽器再使用本地緩存.

結(jié)論: 需要設(shè)置 200 from cache. 這樣才是解決問題之道.

所以說咽弦,其實200(from cache)并沒有重新請求下載圖片,而是和304是相似的原理胁出,就是瀏覽器并沒有重新下載圖片型型,只是用了本地緩存,只不過瀏覽器會多了一重請求驗證全蝶。至于這個具體是返回200from cache還是304闹蒜,貌似需要看服務(wù)器返回的東西是否有設(shè)置ETag值了(這部分需要詳細了解的話,請自行查閱資料)抑淫。

2绷落、initiator是請求發(fā)起者,說白了就是你在哪里執(zhí)行了設(shè)置img.src屬性或者設(shè)置了bgi始苇。在扭蛋項目中砌烁,筆者進行了預(yù)加載(由trueload.js發(fā)起)完成之后,馬上執(zhí)行了一段由index.js執(zhí)行的設(shè)置new Image()數(shù)組保存圖片這么一個操作催式,用于后面的序列幀動畫函喉。而其實initiator:other標(biāo)明的就是一些html的資源文件,包括了js荣月,html函似,css之類,所以圖5這里的other喉童,就是index.js。相同的顿天,在騰訊外星人h5中的首次請求和重復(fù)請求的發(fā)起者分別就是pxload.js和index.js(indexjs中執(zhí)行到了設(shè)置bgi的代碼)堂氯。

3、核心關(guān)鍵

size標(biāo)明牌废,304的時候咽白,瀏覽器會向服務(wù)器發(fā)起請求,這個請求容量非常小鸟缕,相對于200完整請求的20k完整圖片而且晶框,只有20B左右的大小,實際上懂从,它返回的是304的結(jié)果本身授段,而不是圖片數(shù)據(jù)。

重中之重的是這個from menory cache(內(nèi)存緩存)from disk cache(磁盤緩存)

參考鏈接:http://blog.csdn.net/myloveyaqiong/article/details/52762795

(圖6)文中提及番甩,安卓系統(tǒng)對于網(wǎng)絡(luò)圖片緩存機制是侵贵,優(yōu)先讀取Memorycache,找不到之后會讀取Diskcache缘薛,最后都沒有才會發(fā)起網(wǎng)絡(luò)請求窍育。

翻閱《webkit技術(shù)內(nèi)幕》卡睦,書中提及,瀏覽器也是如此漱抓,當(dāng)m cache和d cache中都沒找到資源時表锻,會發(fā)起網(wǎng)絡(luò)請求獲取最新資源。

a6.png

圖6

成因剖析(第三部分):結(jié)論總結(jié)

一乞娄、回顧問題假設(shè)一瞬逊,答案是否定的。原因是進了頁面以后补胚,即使用了預(yù)加載技術(shù)码耐,在后面的邏輯中再次創(chuàng)建并設(shè)置新的img.src或者bgi時,依然要經(jīng)過上面提及的圖片資源獲取步驟:

優(yōu)先讀取Memorycache溶其,找不到之后會讀取Diskcache骚腥,最后都沒有才會發(fā)起網(wǎng)絡(luò)請求。也就是說瓶逃,舉例束铭,一開始用了trueload預(yù)加載了圖片a0.jpg之后(此時是trueload發(fā)起),圖片被加入到了內(nèi)存緩存和磁盤緩存中厢绝,而一段時間(或者一段邏輯跑完之后契沫,來到index.js的某段代碼),在index.js里面再次設(shè)置新的img.src時昔汉,由于某種原因懈万,index.js發(fā)起獲取圖片a0.jpg在memorycache內(nèi)存緩存里面找不到了,于是只能去diskcache磁盤緩存里面找靶病,這個時候就會引起一個重復(fù)請求的現(xiàn)象会通。

二、回顧問題假設(shè)二:答案也是否定的娄周。根據(jù)上述獲取資源原理涕侈,同一路徑資源的圖片,即使在預(yù)加載或頁面中有了第一次加載之后煤辨,后續(xù)代碼段中再次訪問該路徑圖片裳涛,依然有可能會造成前者能成功訪問memorycache里的資源,而后者則失敗從而導(dǎo)致要到diskcache里面去找圖众辨,這樣的情況端三。

三、筆者尚不熟悉m cache的d cache處理數(shù)據(jù)的具體原理鹃彻,尚未弄清為何導(dǎo)致memorycache內(nèi)圖片資源丟失技肩,或者訪問不了的情況,導(dǎo)致別的邏輯代碼在二次訪問的時候需要去d cache里面找資源,這可能和時間虚婿,系統(tǒng)內(nèi)存旋奢,或者initiator請求發(fā)起者有關(guān)。還請各路大神指教一下然痊。

四至朗、至于返回結(jié)果是200from cache還是304,則需要服務(wù)器那邊設(shè)置剧浸。但是可以明確一點的是锹引,如果圖片在訪問頁面時被請求成果過一次之后,那么后續(xù)再次訪問則會有緩存唆香,無論了是否重新發(fā)出請求嫌变,磁盤緩存中已經(jīng)是有緩存到的了,所以在一定程度上已經(jīng)是成功達到了圖片預(yù)加載的目的躬它。也就是說即使是重復(fù)請求了腾啥,那也是返回200from cache還是304,能夠快速使用緩存冯吓,不必重新加載200成功完成圖片請求倘待。只不過再進一步優(yōu)化的話,就是重復(fù)請求都不用了而已组贺。

解決方案:(暫時只提供思路凸舵,后面會補上詳細說明以及demo等)

一、采用專業(yè)的圖片預(yù)加載插件失尖,會比自己重新寫的預(yù)加載函數(shù)要嚴(yán)謹(jǐn)和高效(高手請忽略)啊奄,一般移動推薦pxloader,pc推薦jq的imgpreload掀潮。

二增热、一般情況下,大家只會在預(yù)加載時load一遍圖片胧辽,然后后面訪問圖片時就直接設(shè)置img.src或者bgi:url(地址),這樣在通常情況沒有問題公黑,但是有可能在一定概率下發(fā)生重復(fù)請求邑商。建議改成在load圖片時,把需要load的圖片中的重點圖片凡蚜,比如序列幀這種需要同一個階段大量調(diào)用一組圖片人断,這時就可以把這組圖片保存在一個圖片數(shù)組變量中imgs[ ],然后后面就直接使用這個imgs[ ]朝蜘,不要重新創(chuàng)建對象再賦值圖片地址恶迈。

三、調(diào)整預(yù)加載和訪問的代碼邏輯順序。如果不想像方案二那樣浪費一個全局變量暇仲,可以再頁面loading頁加載完成后步做,不要馬上調(diào)用“設(shè)置imgsrc保存到新數(shù)組,以便后續(xù)調(diào)用”的邏輯代碼奈附,而應(yīng)該放到后面業(yè)務(wù)中的全度,比如點擊按鈕之后才調(diào)用,這樣可以解決剛進入頁面時并發(fā)請求過多導(dǎo)致頁面卡頓的問題斥滤。

四将鸵、如果是采用bgi較多的項目,在調(diào)用預(yù)加載函數(shù)時佑颇,同時加載css顶掉,會發(fā)生css內(nèi)的bgi地址和預(yù)加載js調(diào)用的地址相撞上,而發(fā)生重復(fù)請求的想象挑胸。所以痒筒,解決方法是,進頁面時先把該css文件設(shè)置disabled=""嗜暴,屏蔽掉css加載凸克,完了之后再remove掉disabled屬性。

五闷沥、根據(jù)垃圾回收機制萎战,可以把只調(diào)用一次的大量圖片集,用全局圖片數(shù)組變量保存起來舆逃,并在完成業(yè)務(wù)調(diào)用后蚂维,清除該全局變量imgs=null,回收不必要的內(nèi)存路狮。

其他補充:

待定

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末虫啥,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子奄妨,更是在濱河造成了極大的恐慌涂籽,老刑警劉巖,帶你破解...
    沈念sama閱讀 210,914評論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件砸抛,死亡現(xiàn)場離奇詭異评雌,居然都是意外死亡,警方通過查閱死者的電腦和手機直焙,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 89,935評論 2 383
  • 文/潘曉璐 我一進店門景东,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人奔誓,你說我怎么就攤上這事斤吐。” “怎么了?”我有些...
    開封第一講書人閱讀 156,531評論 0 345
  • 文/不壞的土叔 我叫張陵和措,是天一觀的道長庄呈。 經(jīng)常有香客問我,道長臼婆,這世上最難降的妖魔是什么抒痒? 我笑而不...
    開封第一講書人閱讀 56,309評論 1 282
  • 正文 為了忘掉前任,我火速辦了婚禮颁褂,結(jié)果婚禮上故响,老公的妹妹穿的比我還像新娘。我一直安慰自己颁独,他們只是感情好彩届,可當(dāng)我...
    茶點故事閱讀 65,381評論 5 384
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著誓酒,像睡著了一般樟蠕。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上靠柑,一...
    開封第一講書人閱讀 49,730評論 1 289
  • 那天寨辩,我揣著相機與錄音掠哥,去河邊找鬼扔嵌。 笑死瘤袖,一個胖子當(dāng)著我的面吹牛锰茉,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播饰恕,決...
    沈念sama閱讀 38,882評論 3 404
  • 文/蒼蘭香墨 我猛地睜開眼博敬,長吁一口氣:“原來是場噩夢啊……” “哼灰伟!你這毒婦竟也來了腮恩?” 一聲冷哼從身側(cè)響起梢杭,我...
    開封第一講書人閱讀 37,643評論 0 266
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎秸滴,沒想到半個月后武契,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,095評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡荡含,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,448評論 2 325
  • 正文 我和宋清朗相戀三年咒唆,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片内颗。...
    茶點故事閱讀 38,566評論 1 339
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖敦腔,靈堂內(nèi)的尸體忽然破棺而出均澳,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 34,253評論 4 328
  • 正文 年R本政府宣布找前,位于F島的核電站糟袁,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏躺盛。R本人自食惡果不足惜项戴,卻給世界環(huán)境...
    茶點故事閱讀 39,829評論 3 312
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望槽惫。 院中可真熱鬧周叮,春花似錦、人聲如沸界斜。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,715評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽各薇。三九已至项贺,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間峭判,已是汗流浹背开缎。 一陣腳步聲響...
    開封第一講書人閱讀 31,945評論 1 264
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留林螃,地道東北人奕删。 一個月前我還...
    沈念sama閱讀 46,248評論 2 360
  • 正文 我出身青樓,卻偏偏與公主長得像治宣,于是被迫代替她去往敵國和親急侥。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 43,440評論 2 348