前端圖片引入方式神演算

發表於2017-01-11

v2-89369fd35dd5c1a00eb17ebb1f51f80a_b

| 導語 本文只提供推理方式和分析方法,不保證樣本及計算的精準性,慎讀!!!

先闡述一下背景:

我們團隊對於圖片的使用方式有一個明文規定如下:

  1. 凡是需要合併雪碧圖,或是編碼base64的圖片,均放入slice目錄下對應模組目錄裡,gulp-postcss會統一編譯處理。
  2. 直接以單圖形式引入頁面的圖片,放在page/aaa/bbb/img目錄下(aaa表示業務單元,bbb表示具體頁面),使用相對路徑./xxx.png直接引用。
  3. 全域性複用的單圖,放入common/img目錄下。

目錄結構大概是這個樣子:

v2-20f9ad1f44fd42a790699b9b8c6b3b81_b

這就是我們今天的議題。

眾所周知,頁面內圖片的引入方式一般有這3種:雪碧圖,base64內聯,普通單圖。(canvas,svg等非常規方式不在此次議題裡),先簡單分析一下三種方式的優劣勢:

v2-aa50decdf105eb32ce5086763d6c9f50_b

嗯,大概的情況是這樣的,然後我來稍微擴充套件解釋一下:

1. base64圖本身確實無法快取,但是base64圖一般是存在於css裡的,那麼就可以隨著css被快取而實現間接快取,所以它的快取屬性不是“無”。說它“差”是因為並不是直接被當做圖片快取。當然如果是直接寫在html裡的,那就沒法快取了,這個要注意。

2. base64額外增加html/css大小並不是主要問題,問題是,因此造成的渲染堵塞有時候是致命的!而作為圖片檔案載入則不存在這個問題,因為圖片是不會堵塞到html和css載入的,因此也不會影響首屏渲染。(當然了,你非要把img標籤寫在style前面那我只能說,哥,我服~~~~)

瞭解了三種方式的優劣勢之後,對於使用場景簡單歸納一下:

1. 頁面自身獨有的圖片,全部合併成一張雪碧圖。

2. 公共模組或者公共元件,如果包含多張圖片,則各自為陣合併各自的雪碧圖;如果只有一兩個圖片,或者包含有可以被其他模組、元件、頁面複用的圖片,則使用靈活性好的單圖模式或base64模式。

不過這種說法遺留了一個問題:例如所有頁面都有的吊頂區域,假如那裡有一個小圖,注意,是一個喔(如果是很多的話就合併啦),這種時候是直接單圖引入呢?還是base64內嵌到吊頂的css裡?

好像二者都可以是吧,用單圖的好處就是我在首頁快取後,逛其他頁面時候就不用再載入了,當然了首頁就會多一個請求;而用base64模式,會少一個圖片請求,但會增加吊頂css的檔案大小,從而間接增加了首頁的渲染堵塞時間。好吧,又TMD陷入了糾結。。。

別急!

下面我們再對base64模式做一個簡單的分析:

先明確我們對於base64圖片劣勢的控訴點在於,1:丫會增大原始圖片檔案;2:植入css之後會增大css檔案大小。

做一個簡單的實驗,我把幾個全域性經常出現的小圖示,用base64編碼,結果:

平均增大35%

v2-a436ce1211a90047fc374008ea6ff4aa_b

但是!

gzip壓縮後 —— 4%~40%,平均增大22%

v2-a99217fc516477a7f89fdbbb32b34f53_b

當然樣本少是一個問題,但大概的我們還是能看出來一些端倪:base64確實會增大檔案,而且即使做了gzip後增幅還是居高不下。這也是為什麼我們一般不會對大圖片進行base64編碼的原因,假如對一張100KB的圖片編碼,將會增加20-30KB!這是蠻恐怖的了。但我們現在說的是小圖片呀,一個小圖片1KB左右,即使增大30%也就增加三百多位元組而已。

我們思考的更進一步,究竟怎麼樣的檔案大小增幅,是我們可以接受的?

一個常識,普通人的肉眼可識別的視覺暫留是50ms。而根據多年前端實戰經驗,對於網頁渲染速度,肉眼可敏感識別的渲染時長間隔是500ms,所以一般一個css3過渡效果,transition-duration 為0.3s和0.8s才會有顯著區別,而0.3s和0.5s的區別,除了號稱“畫素眼”的重構同學和有細節控的設計師能感知外,一般人很難明顯感知。

那麼因此我們是不是粗略的得出一個結論:對於首屏渲染時間的減少或增加,使用者可明顯感知的變化範圍是50ms-500ms之間,也就是說,即便你優化做得再好,小於50ms的變化,是不會被感知的,另一方面,如果你因為某個原因增加了首屏渲染時間500ms,就會產生一個很大的感官變化。

好了,這麼說來,我們能接受的檔案大小增幅,所導致的首屏渲染時間增加,應該控制在500ms內。對於身處公司內網的我們而言,M/s的速度顯然不用在意這些細節,500ms可以輕輕鬆鬆載入幾MB的資源,就算是普通使用者,現在寬頻整體速度都6得飛起,500ms載入幾百KB應該不成問題吧。

但是!我們不能這麼想啊,做產品的會把使用者當做小白,我們做開發優化是不是也應該假設使用者還停留在撥號上網時代?哈哈哈,開玩笑了,這倒不至於,但我們確實可以假設使用者網速很一般,100kb/s的網頁載入速度,對自己夠狠了吧我。

基於網速100kb/s的假設,500ms可以載入50kb的資源。。。。等等!總感覺哪裡不對!

一個檔案的載入,應該包含了這些個過程:

v2-553cf7177e42e98924ea5c853df02cc9_b

所以我們理論上“500ms可以載入50kb的資源”,指的是download這裡的速度而已,但是一個小圖片從請求到渲染,需要經過請求排隊,請求堵塞,等待響應,下載等眾多環節。。。那麼500ms我們到底能載入多大的檔案呢?這個問題我真的回答不了,因為這涉及到的環境變數太多了,請求堵塞,網速抖動,瀏覽器版本,伺服器速度,dns解析等等都有可能影響到這個結果。這。。。文章寫不下去了怎麼辦。。。不能放棄治療啊!那麼我們乾脆就更加大概一點估算好了,就假設這500ms中,只有250ms是給我們用來下載資源的,那麼100kb/s的速度我們可以下載25kb的資源,嗯。。。。看起來還蠻是合理的呢。。。。

我們多找幾張小圖看一下timing的分佈:(10kb以內)

v2-e43211078c75672253c926a5c1af95f8_b

有沒有發現一個規律?對於10kb以下的小圖而言,下載時間其實幾乎可以忽略不計(1%左右),而真正佔用貸款的是這一次次請求經歷的漫長的流程(請求排隊,請求堵塞,等待響應….)

補充驗證:當圖片大小增加到100kb以上時,下載耗時平均是總耗時的50%不到。

經過上面一大推的推演和樣本測試後,我們得到了一些相對合理的引數值,接下來要拋大招(計算公式)了!

v2-2b9f1e8701c2d315a291d2f55608949a_b

終於!我們拿到了我們想要的計算結果!2.6倍base64圖片總大小的下載時間,是我們增加的首屏負荷。之前我們已經說了,在不影響使用者感官明顯變化的情況下,我們仁慈的允許多500ms的下載時間,在100kb/s的弱網條件下,最終計算出,允許內嵌的base64圖片大小是20kb!20kb!20kb!這和我們剛剛大概估算的25kb很接近啊!看來有些時候計算無力的情況下估算還點靠譜的。。。

機智的我經過一系列估算後,得出了一個拙劣但相當有意義的答案!意義在於,我終於知道什麼大小的圖片叫做小圖片啦!!!不知道這個歷史性難題難倒了多少重構GG!

v2-84d49d2b5f102a1152b5f6ca024a73e8_b

好吧,別打我,我知道我的計算有點暴力。。。。

Anyway!我在文章副標題裡就說了,

本文只提供推理方式和分析方法,不保證樣本及計算的精準性,慎讀!!!

講真,我的切入點和分析方法應該是沒有問題的對吧各位?只是這其中需要計算的數值實在涉及到太多不確定性,我表示暫時受到那麼一丟丟困擾,所以就先估算之,感興趣的同學可以按照此方法重新計算哈。

做這些蛋疼的研究,終歸還是要回歸到業務上的,那麼我們文章開始的疑問是不是已經解決了呢?經過我們一步步的推演和深入淺出,問題基本解決了。

下面簡單歸納一下不同場景所應該使用的圖片引入方式:(正經臉 -_- !!!)

  • 全域性通用的,非特定頁面或模組獨有的圖片,採用單圖或base64方式引入,二者區別如下:
    • 若該圖片在多處使用或圖片本身較大(這類圖總體積大於20kb),則使用單圖模式
    • 若該圖片只有少數地方使用且圖片本身較小(這類圖總體積小於20kb),則使用base64模式
  • 公共模組/元件裡的圖片(假設該模組名為mod-prd)
    • 模組內有N(N>=3)個圖片,則全部放入**slice/mod/prd**裡,使用雪碧圖模式,否則參考全域性通用圖片處理方式
  • 頁面自身獨有的圖片,全部合併成一張雪碧圖

裝逼結束,輕噴~

相關文章