| 導語 本文只提供推理方式和分析方法,不保證樣本及計算的精準性,慎讀!!!
先闡述一下背景:
我們團隊對於圖片的使用方式有一個明文規定如下:
- 凡是需要合併雪碧圖,或是編碼base64的圖片,均放入slice目錄下對應模組目錄裡,gulp-postcss會統一編譯處理。
- 直接以單圖形式引入頁面的圖片,放在page/aaa/bbb/img目錄下(aaa表示業務單元,bbb表示具體頁面),使用相對路徑./xxx.png直接引用。
- 全域性複用的單圖,放入common/img目錄下。
目錄結構大概是這個樣子:
這就是我們今天的議題。
眾所周知,頁面內圖片的引入方式一般有這3種:雪碧圖,base64內聯,普通單圖。(canvas,svg等非常規方式不在此次議題裡),先簡單分析一下三種方式的優劣勢:
嗯,大概的情況是這樣的,然後我來稍微擴充套件解釋一下:
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%
但是!
gzip壓縮後 —— 4%~40%,平均增大22%
當然樣本少是一個問題,但大概的我們還是能看出來一些端倪: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的資源。。。。等等!總感覺哪裡不對!
一個檔案的載入,應該包含了這些個過程:
所以我們理論上“500ms可以載入50kb的資源”,指的是download這裡的速度而已,但是一個小圖片從請求到渲染,需要經過請求排隊,請求堵塞,等待響應,下載等眾多環節。。。那麼500ms我們到底能載入多大的檔案呢?這個問題我真的回答不了,因為這涉及到的環境變數太多了,請求堵塞,網速抖動,瀏覽器版本,伺服器速度,dns解析等等都有可能影響到這個結果。這。。。文章寫不下去了怎麼辦。。。不能放棄治療啊!那麼我們乾脆就更加大概一點估算好了,就假設這500ms中,只有250ms是給我們用來下載資源的,那麼100kb/s的速度我們可以下載25kb的資源,嗯。。。。看起來還蠻是合理的呢。。。。
我們多找幾張小圖看一下timing的分佈:(10kb以內)
有沒有發現一個規律?對於10kb以下的小圖而言,下載時間其實幾乎可以忽略不計(1%左右),而真正佔用貸款的是這一次次請求經歷的漫長的流程(請求排隊,請求堵塞,等待響應….)
補充驗證:當圖片大小增加到100kb以上時,下載耗時平均是總耗時的50%不到。
經過上面一大推的推演和樣本測試後,我們得到了一些相對合理的引數值,接下來要拋大招(計算公式)了!
終於!我們拿到了我們想要的計算結果!2.6倍base64圖片總大小的下載時間,是我們增加的首屏負荷。之前我們已經說了,在不影響使用者感官明顯變化的情況下,我們仁慈的允許多500ms的下載時間,在100kb/s的弱網條件下,最終計算出,允許內嵌的base64圖片大小是20kb!20kb!20kb!這和我們剛剛大概估算的25kb很接近啊!看來有些時候計算無力的情況下估算還點靠譜的。。。
機智的我經過一系列估算後,得出了一個拙劣但相當有意義的答案!意義在於,我終於知道什麼大小的圖片叫做小圖片啦!!!不知道這個歷史性難題難倒了多少重構GG!
好吧,別打我,我知道我的計算有點暴力。。。。
Anyway!我在文章副標題裡就說了,
本文只提供推理方式和分析方法,不保證樣本及計算的精準性,慎讀!!!
講真,我的切入點和分析方法應該是沒有問題的對吧各位?只是這其中需要計算的數值實在涉及到太多不確定性,我表示暫時受到那麼一丟丟困擾,所以就先估算之,感興趣的同學可以按照此方法重新計算哈。
做這些蛋疼的研究,終歸還是要回歸到業務上的,那麼我們文章開始的疑問是不是已經解決了呢?經過我們一步步的推演和深入淺出,問題基本解決了。
下面簡單歸納一下不同場景所應該使用的圖片引入方式:(正經臉 -_- !!!)
- 全域性通用的,非特定頁面或模組獨有的圖片,採用單圖或base64方式引入,二者區別如下:
- 若該圖片在多處使用或圖片本身較大(這類圖總體積大於20kb),則使用單圖模式
- 若該圖片只有少數地方使用且圖片本身較小(這類圖總體積小於20kb),則使用base64模式
- 公共模組/元件裡的圖片(假設該模組名為mod-prd)
- 模組內有N(N>=3)個圖片,則全部放入**slice/mod/prd**裡,使用雪碧圖模式,否則參考全域性通用圖片處理方式
- 頁面自身獨有的圖片,全部合併成一張雪碧圖
裝逼結束,輕噴~