本文是系列第四篇。系列文章:
圖片資源,在我們的業務中可謂是佔據了非常大頭的一環,尤其是其對頻寬的消耗是十分巨大的。
對圖片的效能優化及體驗優化在今天就顯得尤為重要。本文,就將從各個方面闡述,在各種新特性滿頭飛的今天,我們可以如何儘可能的對我們的圖片資源,進行效能優化及體驗優化。
繼續下一個章節。本章節,我們來討論下圖片的懶載入與非同步影象解碼方案。
懶載入是一種網頁效能優化的常見方式,它能極大的提升使用者體驗。到今天,現在一張圖片超過幾 M 已經是常見事了。如果每次進入頁面都需要請求頁面上的所有的圖片資源,會較大的影響使用者體驗,對使用者的頻寬也是一種極大的損耗。
所以,圖片懶載入的意義即是,當頁面未捲動到相應區域,該區域內的圖片資源(網路請求)不會被載入。反之,當頁面捲動到相應區域,相關圖片資源的請求才會被髮起。
在過去,我們通常都是使用 JavaScript 方案進行圖片的懶載入。而今天,我們在圖片的懶載入實現上,有了更多不一樣的選擇。
首先,回顧一下過往最常見的,使用 JavaScript 方案實現圖片的懶載入。
通過 JavaScript 實現的懶載入,主要是兩種方式:
getBoundingClientRect
API 獲取元素圖片距離視口頂部的距離,配合當前可視區域的位置實現圖片的懶載入IntersectionObserver
API,Intersection Observer(交叉觀察器) 配合監聽元素的 isIntersecting
屬性,判斷元素是否在可視區內,能夠實現比監聽 onscroll 效能更佳的圖片懶載入方案但是,JavaScript 方案的一個劣勢在於,不管如何,需要引入一定量的 JavaScript 程式碼,進行一定量的運算。
到今天,其實我們有更多的其他便捷的方式去實現圖片的懶載入。
content-visibility: auto
實現圖片內容的延遲渲染首先,介紹一個非常有用,但是相對較為冷門的屬性 -- content-visibility
。
content-visibility
:屬性控制一個元素是否渲染其內容,它允許使用者代理(瀏覽器)潛在地省略大量佈局和渲染工作,直到需要它為止。
利用 content-visibility
的特性,我們可以實現如果該元素當前不在螢幕上,則不會渲染其後代元素。
假設我們有這樣一個 DEMO:
<div class="g-wrap">
// 模組 1
<div class="paragraph">
<p>Lorem Start!</p>
<img src="https://s1.ax1x.com/2023/02/20/pSX1xMV.png">
那麼,在新增了 content-visibility: auto
之後,注意觀察頁面的卷軸及捲動效果:
可以看到卷軸在向下捲動在不斷的抽搐,這是由於下面不在可視區域內的內容,一開始是沒有被渲染的,在每次捲動的過程中,才逐漸渲染,以此來提升效能。
Codepen Deom -- content-visibility: auto Image Load Demo
content-visibility: auto
VS 圖片懶載入
當然,其實使用 content-visibility: auto
並不能真正意義上實現圖片的懶載入。
這是因為,即便當前頁面可視區域外的內容未被渲染,但是圖片資源的 HTTP/HTTPS 請求,依然會在頁面一開始被觸發!
因此,這也得到了一個非常重要的結論:
content-visibility: auto
無法直接替代圖片懶載入,設定了 content-visibility: auto
的元素在可視區外只是未被渲染,但是其中的靜態資源仍舊會在頁面初始化的時候被全部載入。因此,它更像是一個虛擬列表的替代方案。
關於 content-visibility
本文限於篇幅,沒有完全展開,但是它是一個非常有意思且對渲染效能有幫助的屬性,完整的教學,你可以看我的這篇文章 -- 使用 content-visibility 優化渲染效能
使用 loading=lazy
HTML 屬性實現圖片懶載入
OK,content-visibility
很不錯,但是略有瑕疵。但是,我們還有其他方式。
HTML5 新增了一個 loading
屬性。
到今天,除了 IE 系列瀏覽器,目前都支援通過 loading
屬性實現延遲載入。此屬性可以新增到 <img>
元素中,也可以新增到 <iframe>
元素中。
屬性的值為 loading=lazy
會告訴瀏覽器,如果影象位於可視區時,則立即載入影象,並在使用者捲動到它們附近時獲取其他影象。
我們可以像是這樣使用它:
<img src="xxx.png" loading="lazy">
這樣,便可以非常便捷的實現圖片的懶載入,省去了新增繁瑣的 JavaScript 程式碼的過程。
看看 loading=lazy
到今天(2023-02-26)的相容性,還是非常不錯的:
使用 decoding=async
實現圖片的非同步解碼
除了 loading=lazy
,HTML5 還新增了一個非常有意思的屬性增強圖片的使用者體驗。那就是 decoding
屬性。
HTMLImageElement 介面的 decoding
屬性用於告訴瀏覽器使用何種方式解析影象資料。
它的可選取值如下:
sync
: 同步解碼影象,保證與其他內容一起顯示。
async
: 非同步解碼影象,加快顯示其他內容。
auto
: 預設模式,表示不偏好解碼模式。由瀏覽器決定哪種方式更適合使用者。
上文其實也提及了,瀏覽器在進行圖片渲染展示的過程中,是需要對圖片檔案進行解碼的,這一個過程快慢與圖片格式有關。
而如果我們不希望圖片的渲染解碼影響頁面的其他內容的展示,可以使用 decoding=async
選項,像是這樣:
<img src="xxx.png" decoding="async">
這樣,瀏覽器便會非同步解碼影象,加快顯示其他內容。這是圖片優化方案中可選的一環。
同樣的,我們來看看到今天(2023-02-26),decoding="async"
的相容性,整體還是非常不錯的,作為漸進增強方案使用,是非常好的選擇。
實際檢驗 loading=lazy
與 decoding=async
效果
OK,下面我們製作一個簡單的 DEMO,試一下 loading=lazy
與 decoding=async
的效果。
我們準備一個擁有 339 個圖片的 HTML 頁面,每個圖片檔案的 src 大小不一。
<div class="g-container">
<img src="image1.jpeg">
<img src="image2.jpeg">
// ... 339 個
</div>
CSS 的設定也很重要,由於是純圖片頁面,如果不給圖片設定預設高寬,最頁面重新整理的一瞬間,<img>
元素的高寬都是 0,會導致所有 <img>
元素都在可視區內,所以,我們需要給 <img>
設定一個預設的高寬:
img {
margin: 8px;
width: 300px;
height: 200px;
object-fit: cover;
}
這樣,再不新增 loading=lazy
與 decoding=async
的狀態下,看看 Network
的表現:
我這裡沒有模擬弱網環境,網速非常快,可以看到,傳送了 339 個圖片資源請求,也就是全部的圖片資源在頁面載入的過程中都請求了,頁面 Load
事件完成的時間為 1.28s。
好,我們給所有的圖片元素,新增上 loading=lazy
與 decoding=async
:
<div class="g-container">
<img src="image1.jpeg" loading="lazy" decoding="async">
<img src="image2.jpeg" loading="lazy" decoding="async">
// ... 339 個
</div>
看看效果:
可以看到,這一次只傳送了 17 個圖片資源請求,頁面 Load
事件完成的時間為 26ms。
優化前
優化後
1.28s
26 ms
1.28s 到 26ms,效果是非常明顯的,如果是弱網環境,對首屏載入效能的提升,會更為明顯!
當然,實際我測試的過程也,也單獨試過 decoding="async"
的作用,只是由於是純圖片頁面,效果不那麼明顯。感興趣的同學,可以自行嘗試。
總結一下
在本章節中,我們介紹了不同的方式實現圖片的懶載入、延遲渲染、非同步解碼,它們分別是:
- 通過 onscroll 事件與
getBoundingClientRect
API 實現圖片的懶載入方案
- 通過 Intersection Observer(交叉觀察器)實現比監聽 onscroll 效能更佳的圖片懶載入方案
- 通過
content-visibility: auto
實現圖片資源的延遲渲染
- 通過
loading=lazy
HTML 屬性實現圖片懶載入
- 通過
decoding=async
HTML 屬性實現圖片的非同步解碼
當然,本文是現代圖片效能優化及體驗優化指南的第四篇,後續將給大家帶來圖片優化的最後一個章節:
- 可存取性 & 圖片資源的容錯及錯誤處理
感興趣的可以提前關注。
最後
OK,本文到此結束,希望本文對你有所幫助