延迟加载视频

发布时间: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>

上例使用值为 nonepreload 属性来阻止浏览器预加载任何视频数据。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 文件的数据量可能会达到数兆字节。画质相近的视频通常要小得多。

使用 <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 是仅使用交叉检测观察器的超轻量级选项。因此,它们具有出色的性能,但需要先进行多重填充,然后才能在旧版浏览器上使用。
  • yall.js 是一个使用 Intersection Observer 并回退到事件处理脚本的库。它还可以使用 data-poster 属性延迟加载视频 poster 图片。
  • 如果您需要 React 专用的延迟加载库,不妨考虑使用 react-lazyload。虽然它不使用 Intersection Observer,但确实为习惯使用 React 开发应用的用户提供了一种熟悉的图片延迟加载方法。

这些延迟加载库都提供了详尽的文档,并提供了大量标记模式,可用于各种延迟加载工作。