[譯] 原生實現圖片懶載入

淚已無痕發表於2019-04-25

在本文中,我們將研究新的 loading 屬性,它為 <img><iframe> 帶來了延遲載入的能力。如果你對此感興趣,可檢視以下示例:

<img src="celebration.jpg" loading="lazy" alt="..." />
<iframe src="video-player.html" loading="lazy"></iframe>
複製程式碼

我們希望在 ~Chrome 75 中為 loading 提供支援,並且我們正在深入研究即將釋出的新特性。在此之前,讓我們深入瞭解它的工作原理。

簡介

Web 頁面通常包含大量的圖片,這些圖片將影響網路流量、頁面尺寸及頁面載入速度。這些圖片中許多處於螢幕外,往往需要使用者滾動頁面才能看到。

過去,為了降低螢幕外的圖片對頁面載入時間的影響,開發人員不得不使用 JavaScript 庫(比如:LazySizes)來推遲這些圖片的載入時機,直到使用者將頁面滾動到它們附近。

[譯] 原生實現圖片懶載入

頁面載入 211 張圖片。沒有延遲載入的版本載入了 10 MB 資料。延遲載入版本(使用 LazySizes)僅預先載入了 250 KB 資料 - 其他圖片將隨著使用者的滾動而載入。檢視 WPT

如果瀏覽器就能幫你做到避免載入螢幕外的圖片呢?這將有助於加快視窗中內容的載入、減少整體網路資料量以及低端裝置下記憶體使用量。因此,我很高興地告訴大家,很快就可以使用 image 和 iframe 的新屬性 loading 來實現了。

loading 屬性

loading 屬性允許瀏覽器推遲載入螢幕外的 image 和 iframe 直到使用者將頁面滾動到它們附近。loading 支援三個值:

  • lazy:延遲載入。
  • eager:立即載入。
  • auto:由瀏覽器來決定是否延遲載入。

如果不指定該屬性,其預設值為 auto。

[譯] 原生實現圖片懶載入

HTML 標準 正在研究將 <img><iframe>loading 屬性作為標準的一部分。

例子

loading 屬性適用於 <img>(包括包含 srcset 屬性及位於 <picture> 內部)和 <iframe>

<!-- 延遲載入螢幕外的圖片直到使用者滾動到它附近 -->
<img src="unicorn.jpg" loading="lazy" alt=".."/>

<!-- 立即載入(而非延遲載入)圖片 -->
<img src="unicorn.jpg" loading="eager" alt=".."/>

<!-- 瀏覽器決定是否延遲載入圖片 -->
<img src="unicorn.jpg" loading="auto" alt=".."/>

<!-- 延遲載入 <picture> 內的圖片。<img> 用來驅動圖片的載入,因此 <picture> 及 srcset
會將合適的圖片呈現在 <img> 上 -->
<picture>
  <source media="(min-width: 40em)" srcset="big.jpg 1x, big-hd.jpg 2x">
  <source srcset="small.jpg 1x, small-hd.jpg 2x">
  <img src="fallback.jpg" loading="lazy">
</picture>

<!-- 延遲載入設定 srcset 屬性的圖片-->
<img src="small.jpg"
     srcset="large.jpg 1024w, medium.jpg 640w, small.jpg 320w"
     sizes="(min-width: 36em) 33.3vw, 100vw"
     alt="A rad wolf" loading="lazy">

<!-- 延遲載入螢幕外的 iframe 直到使用者滾動到它附近 -->
<iframe src="video-player.html" loading="lazy"></iframe>
複製程式碼

由瀏覽器完成“當使用者滾動到附近時”的確切檢測。一般來說,我們希望瀏覽器在快要進入視視窗之前便開始提取延遲圖片和 iframe 的內容。這將增加圖片或 iframe 在使用者滾動到它們時完成載入的更改。

注意:我曾建議應該將 loading 屬性值作為屬性名稱,因為它的命名與 decoding 屬性較為接近。在之前的提議中,類似 lazyload 這樣的屬性沒有被接受,這是因為我們需要支援多個值(lazyeagerauto)。

特性檢測

我們已知道為延遲載入(跨瀏覽器支援)獲取及應用 JavaScript 庫的重要性。loading 的支援情況可以通過以下方式進行檢測:

<script>
if ('loading' in HTMLImageElement.prototype) {
    // 瀏覽器支援 `loading`..
} else {
   // 獲取並應用 polyfill/JavaScript 類庫
   // 來替代 lazy-loading。
}
</script>
複製程式碼

注意:你還可以使用 loading 作為一種漸進的增強功能。支援該屬性的瀏覽器可通過 loading=lazy 獲得新的延遲載入能力,不支援該屬性的瀏覽器仍然會載入圖片。

跨瀏覽器的圖片延遲載入

如果跨瀏覽器支援圖片的延遲載入非常重要,那麼僅僅在使用 <img src=unicorn.jpg loading=lazy /> 的標記中進行特性檢測、使用延遲載入庫是不夠的。

該標記需使用類似 <img data-src=unicorn.jpg />(而非 srcsrcset<source>)的屬性,以避免在不支援新屬性的瀏覽器下觸發立刻載入。如果瀏覽器支援 loading,可以使用 JavaScript 將這些屬性更改為正確的屬性,否則載入類庫。

下面是一個可以說明其可能是什麼樣子的例子。

  • 視窗/一屏展示圖片是常規的 <img> 標籤。data-src 會破壞預載入掃描程式,因此我們希望避免它出現在視窗中的所有內容中。
  • 我們在圖片上使用 data-src 以避免在不支援的瀏覽器中觸發立刻載入,如果瀏覽器支援 loading,我們將 data-src 替換為 src
  • 如果 loading 不被支援,我們載入一個後備(LazySizes)指令碼並啟動它。在這裡,我們用 class=lazyload 向 LazySizes 指出,哪些圖片要延遲載入。
<!-- 讓我們在視窗內正常載入這個圖片 -->
<img src="hero.jpg" alt=".."/>

<!-- 讓我們以延遲載入的方式載入剩餘圖片 -->
<img data-src="unicorn.jpg" loading="lazy" alt=".." class="lazyload"/>
<img data-src="cats.jpg" loading="lazy" alt=".." class="lazyload"/>
<img data-src="dogs.jpg" loading="lazy" alt=".." class="lazyload"/>

<script>
(async () => {
    if ('loading' in HTMLImageElement.prototype) {
        const images = document.querySelectorAll("img.lazyload");
        images.forEach(img => {
            img.src = img.dataset.src;
        });
    } else {
        // 動態引入 LazySizes 庫
        const lazySizesLib = await import('/lazysizes.min.js');
        // 初始化 LazySizes(讀取 data-src & class=lazyload)
        lazySizes.init(); // lazySizes 在全域性環境下工作。
    }
})();
</script>
複製程式碼

示例

看看這個!一個 loading=lazy 示例,展示了整整 100 張小貓圖片

詳見 YouTube 視訊:youtu.be/bhnfL6ODM68

Chrome 實現細節

我們強烈建議等到 loading 屬性處於穩定版本後再在你的生產環境中使用它。早期測試人員可能會發現以下註解非常有用。

立刻嘗試

轉到 chrome://flags 並同時開啟 "Enable lazy frame loading" 和 "Enable lazy image loading",然後重新啟動 Chrome。

配置

Chrome 延遲載入的實現不僅僅基於當前滾動位置的接近程度,還取決於網路連線速度。對於不同的網路連線速度,延遲載入 frame 和圖片的視窗距離閾值是硬編碼的,可以通過命令列覆蓋該值。以下是一個覆蓋圖片延遲載入設定的示例:

canary --user-data-dir="$(mktemp -d)" --enable-features=LazyImageLoading --blink-settings=lazyImageLoadingDistanceThresholdPxUnknown=5000,lazyImageLoadingDistanceThresholdPxOffline=8000,lazyImageLoadingDistanceThresholdPxSlow2G=8000,lazyImageLoadingDistanceThresholdPx2G=6000,lazyImageLoadingDistanceThresholdPx3G=4000,lazyImageLoadingDistanceThresholdPx4G=3000 'https://mathiasbynens.be/demo/img-loading-lazy'
複製程式碼

以上命令對應於(當前)預設配置。將所有值更改為 400 以便僅在滾動位置在距離圖片的 400 畫素以內開始延遲載入。下面我們還可以看到距離閾值設為 1 畫素的另一個做法(在本文前面的視訊中使用):

canary --user-data-dir="$(mktemp -d)" --enable-features=LazyImageLoading --blink-settings=lazyImageLoadingDistanceThresholdPxUnknown=1,lazyImageLoadingDistanceThresholdPxOffline=1,lazyImageLoadingDistanceThresholdPxSlow2G=1,lazyImageLoadingDistanceThresholdPx2G=1,lazyImageLoadingDistanceThresholdPx3G=1,lazyImageLoadingDistanceThresholdPx4G=1 'https://mathiasbynens.be/demo/img-loading-lazy'
複製程式碼

由於實現在未來幾周內穩定下來,我們的預設配置很可能會發生變化。

DevTools

Chrome 中 loading 的一個實現細節是它會在頁面載入時獲取前 2 KB 的圖片資料。如果伺服器支援範圍請求,則前 2 KB 可能包含圖片尺寸。這使得我們能夠生成/顯示具有相同尺寸的佔位符。如果像是圖示一類的資源的話,前 2 KB 也很有可能包含整幅圖片了。

[譯] 原生實現圖片懶載入

Chrome 會在使用者即將看到圖片時抓取其剩餘資料。Chrome DevTools 中要注意的地方是,這可能導致(1)在 DevTools 的網路皮膚中“出現” 兩次獲取和(2)為每個圖片提供兩個請求的資源定時。

服務端確定 loading 支援

在一個美好的世界中,你不需要依賴客戶端上的 JavaScript 特性檢測來決定是否需要載入相容庫 — 你需要在提供包含 JavaScript 延遲載入庫的 HTML 之前處理此問題。客戶端提示可以啟用此類檢查。

傳遞 loading 引數的提示已經被考慮,但目前正處於早期討論階段。

總結

試試看 <img loading>,並讓我們知道你的想法。我對大家如何探索跨瀏覽器的經驗,及是否有任何我們錯過的邊緣情況特別感興趣。

參考資料

感謝 Simon Pieters、Yoav Weiss 和 Mathias Bynens 的反饋。非常感謝 Ben Greenstein、Scott Little、Raj T 和 Houssein Djirdeh 在 LazyLoad 上的工作。

如果發現譯文存在錯誤或其他需要改進的地方,歡迎到 掘金翻譯計劃 對譯文進行修改並 PR,也可獲得相應獎勵積分。文章開頭的 本文永久連結 即為本文在 GitHub 上的 MarkDown 連結。


掘金翻譯計劃 是一個翻譯優質網際網路技術文章的社群,文章來源為 掘金 上的英文分享文章。內容覆蓋 AndroidiOS前端後端區塊鏈產品設計人工智慧等領域,想要檢視更多優質譯文請持續關注 掘金翻譯計劃官方微博知乎專欄

相關文章