延迟加载视频

图片元素一样,您也可以延迟加载视频。视频通常通过 <video> 元素加载(不过,使用 <img> 的替代方法已经出现,但实现方式有限)。不过,延迟加载 <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> 元素提供了一个占位符,用于在视频加载时占据空间。这是因为视频的默认加载行为可能会因浏览器而异:

  • 在 Chrome 中,preload 的默认值以前为 auto,但从 Chrome 64 开始,它现在默认为 metadata。即便如此,在桌面版 Chrome 中,系统可能会使用 Content-Range 标头预加载视频的部分内容。其他基于 Chromium 的浏览器和 Firefox 的行为与之类似。
  • 与桌面版 Chrome 一样,桌面版 Safari 11.0 会预加载一系列视频。 从 11.2 版开始,仅预加载视频元数据。在 iOS 版 Safari 中,系统绝不会预加载视频
  • 启用流量节省程序模式后,preload 默认为 none

由于浏览器在 preload 方面的默认行为并非一成不变,因此最好明确指定该行为。在这种情况下,如果用户发起播放,那么使用 preload="none" 是延迟所有平台上加载视频的最简单方法。preload 属性不是延迟加载视频内容的唯一方式。利用视频预加载功能快速播放,就如何在 JavaScript 中使用视频播放提供一些建议和深刻的见解。

遗憾的是,当您想使用视频代替动画 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 是仅使用 Intersection Observer 的超轻量级选项。因此,它们的性能非常出色,但需要先对它们进行 polyfill 操作,然后才能在旧版浏览器上使用。
  • yall.js 是一个使用 Intersection Observer 并回退到事件处理程序的库。此外,它还可以使用 data-poster 属性延迟加载视频 poster 图片。
  • 如果您需要 React 专用的延迟加载库,可以考虑使用 react-lazyload。虽然它不使用 Intersection Observer,但确实为习惯于使用 React 开发应用的开发者提供了熟悉的图像延迟加载方法。

其中每个延迟加载库都有完备的资料,并提供丰富的标记模式,适用于各种延迟加载工作。