Web圖片資源的加載與渲染時機
此文研究頁面中的圖片資源的加載和渲染時機,使得我們能更好的管理圖片資源,避免不必要的流量和提高用戶體驗。
瀏覽器的工作流程
要研究圖片資源的加載和渲染,我們先要了解瀏覽器的工作原理。以Webkit引擎的工作流程為例:
從上圖可看出,瀏覽器加載一個HTML頁面后進行如下操作:
- 解析HTML —> 構建DOM樹
- 加載樣式 —> 解析樣式 —> 構建樣式規則樹
- 加載javascript —> 執行javascript代碼
- 把DOM樹和樣式規則樹匹配構建渲染樹
- 計算元素位置進行布局
- 繪制
從上圖我們不能很直觀的看出圖片資源從什么時候開始加載,下圖標出圖片加載和渲染的時機:
- 解析HTML【遇到<img>標簽加載圖片】 —> 構建DOM樹
- 加載樣式 —> 解析樣式【遇到背景圖片鏈接不加載】 —> 構建樣式規則樹
- 加載javascript —> 執行javascript代碼
- 把DOM樹和樣式規則樹匹配構建渲染樹【加載渲染樹上的背景圖片】
- 計算元素位置進行布局
- 繪制【開始渲染圖片】
圖片加載與渲染規則
頁面中不是所有的<img>標簽圖片和樣式表背景圖片都會加載。
display:none
- <style>
- .img-purple {
- background-image: url(../image/purple.png);
- }
- </style>
- <img src="../image/pink.png" style="display:none">
- <div class="img-purple" style="display:none"></div>
圖片資源請求如下:
設置了display:none屬性的元素,圖片不會渲染出來,但會加載。
原理
把DOM樹和樣式規則樹匹配構建渲染樹時,會把可渲染元素上的所有屬性(如display:none屬性和background-image屬性)結合一起產出到渲染樹。
當解析渲染樹時會加載<img>標簽元素上的圖片,發現元素上有background-image屬性時會加載背景圖片。
當繪制時發現元素上有display:none屬性,則不計算該元素位置,也不會繪制該元素。
- <style>
- .img-yellow {
- background-image: url(../image/yellow.png);
- }
- </style>
- <div style="display:none">
- <img src="../image/red.png">
- <div class="img-yellow"></div>
- </div>
圖片資源請求如下:
設置了display:none屬性元素的子元素,樣式表中的背景圖片不會渲染出來,也不會加載;而<img>標簽的圖片不會渲染出來,但會加載。
原理
正如上面所說的,構建渲染樹時,只會把可渲染元素產出到渲染樹,這就意味有不可渲染元素,當匹配DOM樹和樣式規則樹時,若發現一個元素的屬性上有display:none,瀏覽器會認為該元素的子元素是不可渲染的,因此不會把該元素的子元素產出到渲染樹上。
當解析渲染樹時渲染樹上沒有設置了display:none屬性元素的子元素,因此不會加載該元素中子元素的背景圖片。
當繪制時也因為渲染樹上沒有設置了display:none屬性元素的子元素,因此該元素中子元素的背景圖片不會渲染出來。
重復圖片
- .img-blue {
- background-image: url(../image/blue.png);
- }
- <div class="img-blue"></div>
- <img src="../image/blue.png">
- <img src="../image/blue.png">
圖片資源請求如下:
頁面中多個<img>標簽或樣式表中的背景圖片圖片路徑是同一個,圖片只加載一次。
原理
瀏覽器請求資源時,都會先判斷是否有緩存,若有緩存且未過期則會從緩存中讀取,不會再次請求。先加載的圖片會存儲到瀏覽器緩存中,后面再次請求同路徑圖片時會直接讀取緩存中的圖片。
不存在元素的背景圖片
- .img-blue {
- background-image: url(../image/blue.png);
- }
- .img-orange{
- background-image: url(../image/orange.png);
- }
圖片資源請求如下:
不存在元素的背景圖片不會加載。
原理
不存在的元素不會產出到DOM樹上,因此渲染樹上也不會有不存在的元素,當解析渲染樹時無法解析不存在的元素,不存在的元素上的圖片自然不會加載也不會渲染。
偽類的背景圖片
- .img-green {
- background-image: url(../image/green.png);
- }
- .img-green:hover{
- background-image: url(../image/red.png);
- }
觸發hover前的圖片資源請求如下:
觸發hover后的圖片資源請求如下:
當觸發偽類的時候,偽類樣式上的背景圖片才會加載。
原理
觸發hover前,DOM樹與樣式規則樹匹配的是無hover狀態選擇器.img-green的樣式,因此渲染樹上background-image屬性的值是url(../image/green.png),解析渲染樹時加載的是green.png,繪制時渲染的也是green.png。
觸發hover后,因為.img-green:hover的優先級比較高,因此DOM樹與樣式規則樹匹配的是有hover狀態選擇器.img-green:hover的樣式,渲染樹上background-image屬性的值是url(../image/red.png),解析渲染樹時加載的是red.png,繪制時渲染的也是red.png。
應用
占位圖
當使用樣式表中的背景圖片作為占位符時,要把背景圖片轉為base64格式。這是因為背景圖片加載的順序在標簽后面,背景圖片可能會在<img>標簽圖片加載完成后才開始加載,達不到想要的效果。
預加載
很多場景里圖片是在改變或觸發狀態后才顯示出來的,例如點擊一個Tab后,一個設置display:none隱藏的父元素變為顯示,這個父元素里的子元素圖片會在父元素顯示后才開始加載;又如當鼠標hover到圖標后,改變圖標圖片,圖片會在hover上去后才開始加載,導致出現閃一下這種不友好的體驗。
在這種場景下,我們就需要把圖片預加載,預加載有很多種方式:
- 若是小圖標,可以合并成雪碧圖,在改變狀態前就把所有圖標都一起加載了。
- 使用上文講到的,設置了display:none屬性的元素,圖片不會渲染出來,但會加載。把要預加載的圖片加到設置了display:none的元素背景圖或
標簽里。
- 在javascript創建img對象,把圖片url設置到img對象的src屬性里。