延遲載入圖片

圖片可能是因為以 <img> 元素內嵌在 HTML 中,或做為 CSS 背景圖片。本文將介紹如何延遲載入這兩種圖片。

內嵌圖片

最常見的延遲載入候選項目是 <img> 元素中使用的圖片。內嵌圖片提供三種延遲載入選項。您可以將該選項合併使用,以在各瀏覽器之間提供最佳相容性:

使用瀏覽器層級的延遲載入功能

Chrome 和 Firefox 都支援透過 loading 屬性延遲載入。 這個屬性可以新增至 <img> 元素,也可以新增至 <iframe> 元素。lazy 的值會指示瀏覽器立即載入位於可視區域的圖片,並在使用者捲動接近圖片時擷取其他圖片。

如要進一步瞭解瀏覽器支援,請參閱 MDN 瀏覽器相容性表格中的 loading 欄位。如果瀏覽器不支援延遲載入,系統會忽略這個屬性,並照常載入圖片。

對於大多數網站而言,將這項屬性新增至內嵌圖片可大幅提升效能,並讓使用者能載入可能沒捲動過的圖片。如果您有大量圖片,並想確保瀏覽器使用者不支援延遲載入功能,就必須將圖片與下方說明的方法結合。

詳情請參閱適用於網頁的瀏覽器層級延遲載入

使用 Intersection Observer

為了替 <img> 元素執行折線延遲載入,我們會使用 JavaScript 檢查這些元素是否在可視區域。如果有,其中的 src (有時是 srcset) 屬性會填入所需圖片內容的網址。

如果您之前曾編寫過延遲載入程式碼,可能已使用 scrollresize 等事件處理常式來完成工作。雖然這個方法在各種瀏覽器中是最相容的方法,但新式瀏覽器透過 Intersection Observer API 檢查元素可見度更有效率和效率。

Intersection Observer 比依賴各種事件處理常式的程式碼更容易使用和讀取,因為您只需要註冊觀察器就能查看元素,而不需要編寫繁瑣的元素可見度偵測程式碼。接下來只須決定元素顯示時的處理方式。假設延遲載入的 <img> 元素採用這個基本標記模式:

<img class="lazy" src="placeholder-image.jpg" data-src="image-to-lazy-load-1x.jpg" data-srcset="image-to-lazy-load-2x.jpg 2x, image-to-lazy-load-1x.jpg 1x" alt="I'm an image!">

因此,請特別留意以下三個相關的標記:

  1. class 屬性,您可以在 JavaScript 中選取元素。
  2. src 屬性會參照網頁首次載入時顯示的預留位置圖片。
  3. data-srcdata-srcset 屬性是預留位置屬性,其中包含在元素進入可視區域後載入的圖片網址。

現在來看看如何在 JavaScript 中使用 Intersection Observer,透過此標記模式延遲載入圖片:

document.addEventListener("DOMContentLoaded", function() {
  var lazyImages = [].slice.call(document.querySelectorAll("img.lazy"));

  if ("IntersectionObserver" in window) {
    let lazyImageObserver = new IntersectionObserver(function(entries, observer) {
      entries.forEach(function(entry) {
        if (entry.isIntersecting) {
          let lazyImage = entry.target;
          lazyImage.src = lazyImage.dataset.src;
          lazyImage.srcset = lazyImage.dataset.srcset;
          lazyImage.classList.remove("lazy");
          lazyImageObserver.unobserve(lazyImage);
        }
      });
    });

    lazyImages.forEach(function(lazyImage) {
      lazyImageObserver.observe(lazyImage);
    });
  } else {
    // Possibly fall back to event handlers here
  }
});

在文件的 DOMContentLoaded 事件中,此指令碼會查詢所有具備 lazy 類別之 <img> 元素的 DOM。如果有 Intersection Observer,請建立新的觀察器,以便在 img.lazy 元素進入可視區域時執行回呼。

所有新版瀏覽器皆可使用 Intersection Observer。因此,您可以將其做為 loading="lazy" 的 polyfill,確保大多數訪客都能使用延遲載入功能。

CSS 中的圖片

雖然 <img> 標記是在網頁上使用圖片最常見的方式,但您也可以透過 CSS background-image 屬性 (和其他屬性) 叫用圖片。瀏覽器層級延遲載入不適用於 CSS 背景圖片,因此如果您有可延遲載入的背景圖片,就必須考慮其他方法。

無論 <img> 元素是否載入,CSS 圖片載入行為都是進行更多推測,建構文件和 CSS 物件模型轉譯樹狀結構時,瀏覽器會先檢查 CSS 套用至文件的方式,然後再要求外部資源。如果瀏覽器判定含有外部資源的 CSS 規則不適用於文件目前建構中的文件,瀏覽器就不會要求該文件。

這種推測行為可以使用 JavaScript 判斷元素位於可視區域的時間,然後對該元素套用會叫用背景圖片的樣式,進而延遲在 CSS 中載入圖片。這會導致系統在需要時下載圖片,而非在初始載入時下載。舉例來說,我們可以有一個包含大型主頁背景圖片的元素:

<div class="lazy-background">
  <h1>Here's a hero heading to get your attention!</h1>
  <p>Here's hero copy to convince you to buy a thing!</p>
  <a href="/buy-a-thing">Buy a thing!</a>
</div>

div.lazy-background 元素通常會包含部分 CSS 叫用的主頁背景圖片。不過,在這個延遲載入範例中,您可以在元素位於可視區域時,透過新增至元素的 visible 類別隔離 div.lazy-background 元素的 background-image 屬性:

.lazy-background {
  background-image: url("hero-placeholder.jpg"); /* Placeholder image */
}

.lazy-background.visible {
  background-image: url("hero.jpg"); /* The final image */
}

接著,請使用 JavaScript 檢查元素是否在可視區域中 (透過 Intersection Observer!),並在 div.lazy-background 元素中加入 visible 類別,該元素會載入圖片:

document.addEventListener("DOMContentLoaded", function() {
  var lazyBackgrounds = [].slice.call(document.querySelectorAll(".lazy-background"));

  if ("IntersectionObserver" in window) {
    let lazyBackgroundObserver = new IntersectionObserver(function(entries, observer) {
      entries.forEach(function(entry) {
        if (entry.isIntersecting) {
          entry.target.classList.add("visible");
          lazyBackgroundObserver.unobserve(entry.target);
        }
      });
    });

    lazyBackgrounds.forEach(function(lazyBackground) {
      lazyBackgroundObserver.observe(lazyBackground);
    });
  }
});

對最大內容繪製 (LCP) 的影響

延遲載入是很好的最佳化效果,可延遲載入圖片到實際需要的時間,從而減少啟動期間的整體數據用量和網路爭用情形。這麼做可以縮短圖片解碼所需的時間,縮短啟動時間,並減少主要執行緒的處理作業。

不過,如果您深入瞭解這項技術,延遲載入就可能會對網站的最大內容繪製 LCP 產生負面影響。請避免在啟動期間延遲載入可視區域中的圖片。

使用以 JavaScript 為基礎的延遲載入器時,請避免延遲載入可視區域圖片,因為這些解決方案通常會使用 data-srcdata-srcset 屬性做為 srcsrcset 屬性的預留位置。但問題在於,瀏覽器預先載入掃描器在啟動期間找不到圖片,導致載入延遲。

即使使用瀏覽器層級的延遲載入來延遲載入可視區域圖片,也可能會回溯。如果 loading="lazy" 套用至可視區域圖片,系統會延遲圖片,直到瀏覽器確定圖片位於可視區域中,因此會影響網頁的 LCP。

切勿延遲載入啟動時顯示在可視區域的圖片。該模式會對網站的 LCP 產生負面影響,進而讓使用者享有不佳的體驗。如果啟動時需要圖片,請不要延遲載入,在啟動時盡快載入圖片!

延遲載入程式庫

請盡可能使用瀏覽器層級的延遲載入功能,但如果您發現自己遇到無法採用的功能 (例如有一大群使用者仍依賴舊版瀏覽器),可以使用下列程式庫延遲載入圖片:

  • lazysizes 是功能完整的延遲載入程式庫,可延遲載入圖片和 iframe。這個模式使用的模式與此處顯示的程式碼範例十分類似,因為該範例會自動繫結至 <img> 元素的 lazyload 類別,而且您必須在 data-src 和/或 data-srcset 屬性中指定圖片網址,將這些內容分別替換為 src 和/或 srcset 屬性。它使用 Intersection Observer (您可以進行聚填入),而且還能使用多個外掛程式進行擴充,以執行延遲載入影片等操作。進一步瞭解如何使用延遲
  • vanilla-lazyload 是輕量級的選項,適用於延遲載入圖片、背景圖片、影片、iframe 和指令碼。它運用 Intersection Observer,支援回應式圖片,並支援瀏覽器層級的延遲載入功能。
  • lozad.js 是另一個僅使用 Intersection Observer 的輕量選項。因此,這樣做可提供高效能,但需要經過多填處理,才能在舊版瀏覽器中使用。
  • 如果需要 React 專用的延遲載入程式庫,請考慮回應延遲載入。雖然並未使用 Intersection Observer,但它針對那些習慣透過 React 開發應用程式的人員,提供熟悉的延遲載入映像檔