发布时间: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>
上例使用值为 none
的 preload
属性来阻止浏览器预加载任何视频数据。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 具有以下三个特点:
- 加载时自动播放。
- 连续循环播放(但并非始终如此)。
- 没有音轨。
使用 <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>
autoplay
、muted
和 loop
属性的含义不言而喻。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-lazyload 和 lozad.js 是超轻量级选项,仅使用 Intersection Observer。因此,它们的性能极佳,但如果要在旧版浏览器上使用,则需要 polyfill。
- yall.js 是一个使用 Intersection Observer 并回退到事件处理程序的库。它还可以使用
data-poster
属性延迟加载视频poster
图片。 - 如果您需要 React 专用的延迟加载库,不妨考虑使用 react-lazyload。虽然该库不使用 Intersection Observer,但确实为习惯于使用 React 开发应用的开发者提供了熟悉的图像延迟加载方法。
上述每个延迟加载库都有完备的资料,并提供丰富的标记模式,适用于各种延迟加载工作。