延遲載入最佳做法

雖然延遲載入圖片和影片對效能有良好且可評估成效的好處,但這不是輕微的工作。如果作業出錯,可能會導致非預期的結果。因此,請特別注意以下幾點。

摺疊式裝置

您可能會想要使用 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 屬性指定的值看起來很像,那是因為這樣!在這種情況下,觀察到的元素底部邊界 (瀏覽器可視區域預設為瀏覽器可視區域,但可以使用 root 屬性變更為特定元素),並增加 256 像素。這表示當圖片元素在可視區域的 256 像素以內時,就會執行回呼函式,且圖片會在使用者實際看見圖片前開始載入。

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

版面配置位移和預留位置

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

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

圖片解碼延遲

在 JavaScript 中載入大型圖片並拖放到 DOM 時,可能會串連主要執行緒,導致使用者介面在解碼期間短時間內沒有回應。先使用 decode 方法以非同步方式將圖片解碼,再將圖片插入 DOM 前,可能會減少這類卡頓問題,但需要注意的是:目前尚未開放所有國家/地區使用,這會讓延遲載入邏輯變得更加複雜。如要使用,請自行檢查。以下說明如何使用 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 隨時皆可使用。如果您要延遲載入圖片,建議您提供 <noscript> 標記,以便在 JavaScript 無法使用時顯示圖片。最簡單的備用示例包括在 JavaScript 關閉時使用 <noscript> 元素提供圖片:

我是圖片!

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

<html class="no-js">

接著,在 <head> 中放置一行內嵌指令碼,然後透過 <link> 標記要求任何樣式表。當 JavaScript 開啟時,會從 <html> 元素移除 no-js 類別:

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

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

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

這樣做不會妨礙載入預留位置圖片,但結果較為理想。關閉 JavaScript 的使用者會看到預留位置圖片,比預留位置圖片更好,而且根本沒有有意義的圖片內容。