延遲載入最佳做法

雖然延遲載入圖片和影片有正面且可量化的效能優勢,但它並非輕盈的任務。因此如果出錯 可能會產生非預期的結果因此,請務必留意以下疑慮。

大膽嘗試

您可能很想在使用 JavaScript 對網頁上的每個媒體資源都進行延遲載入,但您必須設法抵抗這項誘惑。位於不需捲動位置的任何內容都不應延遲載入。請將這類資源視為重要資產,因此應正常載入。

延遲載入會延遲載入資源,直到 DOM 互動且指令碼完成載入並開始執行為止。至於需捲動位置的圖片,這就沒有問題,但位於不需捲動位置的重要資源應與標準 <img> 元素一併載入,以便盡快顯示。

當然,在許多不同大小的螢幕上,使用者經常會瀏覽網站,但需捲動位置並不明確。例如,筆電上不需捲動位置的設定 可能會放在行動裝置的下方。關於如何以最佳方式解決這個狀況,原則上並沒有相應的建議。您需要列出網頁的重要資產目錄,並以一般方式載入這些圖片。

此外,我們也建議您嚴格把摺疊線視為觸發延遲載入的門檻。這個做法可能比較理想,是在使用者捲動位於可視區域前建立一定距離的緩衝區區域,方便圖片在使用者捲動至可視區域前就開始載入。舉例來說,Intersection Observer API 可讓您在建立新的 IntersectionObserver 執行個體時,在選項物件中指定 rootMargin 屬性。這樣可以有效為元素提供緩衝區,在元素進入可視區域前,觸發延遲載入行為:

let lazyImageObserver = new IntersectionObserver(function(entries, observer) {
  // lazy-loading image code goes here
}, {
  rootMargin: "0px 0px 256px 0px"
});

如果 rootMargin 的值與您為 CSS margin 屬性指定的值類似,那就是這樣!在這種情況下,觀察到元素的底部邊界會擴大 256 像素 (預設為瀏覽器可視區域,但可以使用 root 屬性變更為特定元素)。這表示回呼函式會在可視區域的 256 像素以內時執行回呼函式,並在使用者實際看到圖片之前開始載入圖片。

如要在不支援 Intersection Observe 的瀏覽器中達成相同效果,請使用捲動事件處理程式碼,並調整 getBoundingClientRect 檢查以加入緩衝區。

版面配置位移和預留位置

如未使用預留位置,延遲載入媒體可能會導致版面配置中位移。這些變更可能會對使用者造成困擾,並會觸發會耗用系統資源並導致卡頓的高成本 DOM 版面配置作業。請至少考慮使用與目標圖片相同尺寸的單色預留位置,或使用 LQIPSQIP 等技巧,在載入媒體項目前提供內容提示。

如果是 <img> 標記,src 一開始應指向預留位置,直到該屬性更新為最終圖片網址。在 <video> 元素中使用 poster 屬性指向預留位置圖片。此外,請同時對 <img><video> 標記使用 widthheight 屬性。這可確保從預留位置轉換至最終圖片,不會隨著媒體載入而變更元素的算繪大小。

圖片解碼延遲

在 JavaScript 中載入大型圖片並將圖片放入 DOM 後,就會與主要執行緒繫結,導致使用者介面在解碼時短暫沒有回應。在將圖片插入 DOM 之前,先使用 decode 方法以非同步方式解碼圖片,可能會減少這類卡頓問題,但請注意:目前這項功能未在所有地區推出,而且會增加延遲載入邏輯的複雜度。如要使用,請先進行檢查。以下說明如何將 Image.decode() 與備用搭配使用:

var newImage = new Image();
newImage.src = "my-awesome-image.jpg";

if ("decode" in newImage) {
  // Fancy decoding logic
  newImage.decode().then(function() {
    imageContainer.appendChild(newImage);
  });
} else {
  // Regular image load
  imageContainer.appendChild(newImage);
}

如要查看類似本範例的程式碼,請參閱這個 CodePen 連結。如果您大部分的圖片都很小,這可能對您沒有什麼影響,但在您延遲載入大型圖片並將圖片插入 DOM 時,確實可以減少卡頓。

無法載入檔案時

有時媒體資源載入失敗的原因可能是某個原因或其他因素,請問是什麼時候?這取決於您的假設情況,但以下舉例說明:您有一個短時間的 HTML 快取政策 (例如五分鐘),使用者造訪網站「或」使用者長時間保持開啟過時分頁 (例如數小時),並返回讀取內容。在這個過程中的某個時間點,系統會重新部署。在這項部署期間,圖片資源的名稱會因雜湊型版本管理而變更,或是一併移除。當使用者延遲載入圖片時,資源將無法使用,因而失敗。

雖然這種情況相對罕見,但如果您遇到延遲載入失敗的情況,建議您準備備份方案。就圖片而言,這類解決方案大致如下:

var newImage = new Image();
newImage.src = "my-awesome-image.jpg";

newImage.onerror = function(){
  // Decide what to do on error
};
newImage.onload = function(){
  // Load the image
};

如果發生錯誤,您應該怎麼做,視您的應用程式而定。例如,您可以將圖片預留位置區域替換成可讓使用者再次嘗試載入圖片的按鈕,或只是在圖片預留位置區域中顯示錯誤訊息。

也可能會發生其他情況。無論您採取何種行動,在發生錯誤時告知使用者,並非明智的想法,而是讓他們在發生問題時採取行動。

JavaScript 可用性

請勿假設 JavaScript 隨時可用。如果您要延遲載入圖片,請考慮提供可在 JavaScript 無法使用的情況下顯示圖片的 <noscript> 標記。最簡單的備用範例包括在 JavaScript 關閉的情況下,使用 <noscript> 元素提供圖片:

我是圖片!

如果關閉 JavaScript,使用者將「同時」看到預留位置圖片和 <noscript> 元素包含的圖片。如要解決這個問題,請將 no-js 類別放置在 <html> 標記上,如下所示:

<html class="no-js">

接著,在要求任何樣式表前,<head>使用<link>標記從 <html> 元素中移除 no-js 類別 (如果 JavaScript 已啟用):

<script>document.documentElement.classList.remove("no-js");</script>

最後,在無法使用 JavaScript 時,使用某些 CSS 隱藏具有延遲類別的元素:

.no-js .lazy {
  display: none;
}

雖然這不會導致預留位置圖片無法載入,但結果較適合理想。關閉 JavaScript 的使用者只能取得預留位置圖片,這比預留位置圖片更好,從根本沒有有意義的圖片內容。