延遲載入影片

發布日期:2019 年 8 月 16 日

如同圖片元素,您可能也想使用延遲載入影片。影片通常會透過 <video> 元素載入,但如果是託管於 YouTube 等其他服務的影片,則可能會使用 <iframe> (如果是這種情況,請參閱延遲載入 iframe 一文)。

<video> 的延遲載入方式取決於用途,因為有幾種不同的解決方案。

避免自動播放影片通常是最佳做法,因為這樣使用者可以自行控制。在這些情況下,指定 <video> 元素的 preload 屬性,是避免載入整部影片的最佳方法:

<video controls preload="none" poster="one-does-not-simply-placeholder.jpg">
  <source src="one-does-not-simply.webm" type="video/webm">
  <source src="one-does-not-simply.mp4" type="video/mp4">
</video>

上一個範例使用 preload 屬性,其值為 none,可防止瀏覽器預先載入任何影片資料。poster 屬性會為 <video> 元素提供預留位置,在影片載入期間佔用該空間。

在大多數瀏覽器中,preload 預設為 metadata,並使用 Content-Range 標頭預先載入部分影片。這可能會導致下載的資料量超出預期,尤其是在瀏覽器不支援 Content-Range 標頭的情況下。即使瀏覽器支援這項功能,也無法得知中繼資料儲存在哪個位元組,而且可能不會儲存在檔案開頭。因此,如要避免載入影片,請務必指定 none 並使用 preload="none"

這項功能還可進一步強化,在使用者將游標懸停在影片上時,透過 onmouseenter 屬性 (或等同的 mouseenter 事件處理常式) 預先載入中繼資料:

<video controls
  preload="none"
  poster="one-does-not-simply-placeholder.jpg"
  onmouseenter="event.target.setAttribute('preload','metadata')">
  <source src="one-does-not-simply.webm" type="video/webm">
  <source src="one-does-not-simply.mp4" type="video/mp4">
</video>

這麼做不僅可縮短使用者播放影片時的延遲時間,還可在使用者點選影片時立即顯示影片長度。

影片可符合LCP 候選條件poster 圖片的載入速度比影片快,因此如果要將其設為 LCP 候選項目,建議您使用海報圖片,並預先載入fetchpriority 屬性值為 "high"

<link rel="preload" href="one-does-not-simply-placeholder.jpg" as="image" fetchpriority="high">
<video controls preload="none"
  poster="one-does-not-simply-placeholder.jpg"
  onmouseenter="event.target.setAttribute('preload','metadata')">
  <source src="one-does-not-simply.webm" type="video/webm">
  <source src="one-does-not-simply.mp4" type="video/mp4">
</video>

影片用於取代 GIF 動畫

自動播放影片最常用於 GIF 風格的快速動畫。雖然 GIF 動畫廣受歡迎,但在許多方面 (尤其是檔案大小) 都比不上影片。動畫 GIF 檔案可能會佔用數 MB 的資料空間。畫質相近的影片通常會小得多。

使用 <video> 元素取代 GIF 動畫,不如使用 <img> 元素那麼簡單。GIF 動畫有三個特徵:

  1. 載入時會自動播放。
  2. 這些檔案會持續循環播放 (但不一定如此)。
  3. 沒有音軌。

使用 <video> 元素達成這項目標的做法如下:

<video autoplay muted loop playsinline>
  <source src="one-does-not-simply.webm" type="video/webm">
  <source src="one-does-not-simply.mp4" type="video/mp4">
</video>

autoplaymutedloop 屬性名稱不言自明。playsinline 是 iOS 自動播放功能的必要條件。您現在擁有可跨平台使用的影片為 GIF 替代方案。不過,該如何延遲載入?首先,請視情況修改 <video> 標記:

<video class="lazy" autoplay muted loop playsinline width="610" height="254" poster="one-does-not-simply.jpg">
  <source data-src="one-does-not-simply.webm" type="video/webm">
  <source data-src="one-does-not-simply.mp4" type="video/mp4">
</video>

您會發現新增了 poster 屬性,可讓您指定預留位置,在影片延後載入前,預留位置會佔用 <video> 元素的空間。如同 <img> 延遲載入範例,請將影片網址儲存在每個 <source> 元素的 data-src 屬性中。接著,請使用類似於以 Intersection Observer 為基礎的圖片延遲載入範例的 JavaScript 程式碼:

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

  if ("IntersectionObserver" in window) {
    var lazyVideoObserver = new IntersectionObserver(function(entries, observer) {
      entries.forEach(function(video) {
        if (video.isIntersecting) {
          for (var source in video.target.children) {
            var videoSource = video.target.children[source];
            if (typeof videoSource.tagName === "string" && videoSource.tagName === "SOURCE") {
              videoSource.src = videoSource.dataset.src;
            }
          }

          video.target.load();
          video.target.classList.remove("lazy");
          lazyVideoObserver.unobserve(video.target);
        }
      });
    });

    lazyVideos.forEach(function(lazyVideo) {
      lazyVideoObserver.observe(lazyVideo);
    });
  }
});

當您延遲載入 <video> 元素時,您需要逐一檢查所有子項 <source> 元素,並將其 data-src 屬性切換為 src 屬性。完成後,您需要呼叫元素的 load 方法,觸發影片載入作業,之後媒體就會根據 autoplay 屬性自動開始播放。

使用這個方法,您就能取得模擬 GIF 動畫行為的影片解決方案,但不會像 GIF 動畫一樣造成大量資料使用量,而且可以延後載入內容。

延遲載入程式庫

您可以使用下列程式庫來延後載入影片:

  • vanilla-lazyloadlozad.js 都是超輕量選項,只使用 Intersection Observer。因此,這些元素的效能極佳,但必須先進行多重填充,才能在舊版瀏覽器中使用。
  • yall.js 是使用 Intersection Observer 的程式庫,並會改用事件處理常式。也可以使用 data-poster 屬性,以延遲載入方式載入影片 poster 圖片。
  • 如果您需要 React 專用的延後載入程式庫,不妨考慮使用 react-lazyload。雖然它不會使用 Intersection Observer,但確實為習慣使用 React 開發應用程式的人,提供圖片延後載入的熟悉方法。

每個延遲載入程式庫都提供完善的文件,其中包含許多標記模式,可用於各種延遲載入作業。