视频懒加载

发布时间: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 的大小可能会扩展到几兆字节的数据范围。视觉质量相似的视频往往要小得多。

使用 <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> 懒加载示例一样,将视频 URL 存储在每个 <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 的超轻量级选项。因此,它们具有很高的性能,但需要进行 polyfill 才能在旧版浏览器上使用它们。
  • yall.js 是一个使用 Intersection Observer 并回退到事件处理程序的库。它还可以使用 data-poster 属性懒加载视频 poster 图片。
  • 如果您需要特定于 React 的懒加载库,您可以考虑react-lazyload。虽然它不使用 Intersection Observer,但它确实为那些习惯于使用 React 开发应用程序的人提供了熟悉的懒加载图像方法。

这些懒加载库中的每一个都有完善的文档,并为您的各种懒加载工作提供了大量的标记模式。