Carga diferida de videos

Fecha de publicación: 16 de agosto de 2019

Al igual que con los elementos de imagen, te recomendamos que cargues los videos de forma diferida. Por lo general, los videos se cargan con el elemento <video>, aunque en el caso de los videos alojados en otros servicios, como YouTube, es posible que se usen <iframe> (en cuyo caso, consulta el artículo sobre iframes de carga diferida).

La forma de cargar de forma diferida <video> depende del caso de uso, ya que hay un par de soluciones diferentes.

Por lo general, se recomienda evitar la reproducción automática de videos, ya que deja el control en manos del usuario. En estos casos, especificar el atributo preload en el elemento <video> es la mejor manera de evitar cargar todo el video:

<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>

En el ejemplo anterior, se usa un atributo preload con un valor de none para evitar que los navegadores precarguen ningún dato de video. El atributo poster le proporciona al elemento <video> un marcador de posición que ocupará el espacio mientras se carga el video.

En la mayoría de los navegadores, preload se establece de forma predeterminada en metadata y una parte del video se precarga con el encabezado Content-Range. Esto puede provocar que se descarguen más datos de los deseados, en especial si el navegador no admite el encabezado Content-Range. Incluso cuando esto es compatible, los navegadores no pueden saber en qué bytes se almacenan los metadatos, y es posible que no se almacenen al principio del archivo. Por lo tanto, la mejor oportunidad de evitar cargar el video es especificar none y usar preload="none".

Esto se puede mejorar aún más para precargar los metadatos cuando el usuario coloca el cursor sobre el video con un atributo onmouseenter (o con el controlador de eventos mouseenter equivalente):

<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>

Esto no solo reduce la demora cuando el usuario va a reproducir el video, sino que también muestra la duración del video en cuanto lo hace.

Los videos pueden calificar como candidatos para LCP. Una imagen poster se cargará más rápido que el video, por lo que, si es un candidato de LCP, debes usar una imagen de póster, pero también precargarla con un valor del atributo fetchpriority de "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>

Para videos en reemplazo de GIF animados

Los videos con reproducción automática se usan con mayor frecuencia para animaciones rápidas al estilo de GIF. Si bien los GIF animados se utilizan ampliamente, son mediocres equivalentes de los videos en diversas formas, especialmente en el tamaño del archivo. Los GIF animados pueden alcanzar el rango de varios megabytes de datos. Los videos con una calidad visual similar suelen ser mucho más pequeños.

El uso del elemento <video> como reemplazo de un GIF animado no es tan sencillo como el elemento <img>. Los GIF animados tienen tres características:

  1. Se reproducen automáticamente cuando se cargan.
  2. Se repiten de forma continua (aunque no siempre es así).
  3. No tienen una pista de audio.

Lograr esto con el elemento <video> se ve de la siguiente manera:

<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>

Los atributos autoplay, muted y loop se explican por sí solos. playsinline es necesario para que se produzca la reproducción automática en iOS. Ahora tienes un reemplazo de video como GIF que se puede utilizar en todas las plataformas. ¿Pero cómo se debe abordar su carga diferida? Para comenzar, modifica el marcado <video> según corresponda:

<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>

Notarás que se agregó el atributo poster, que te permite especificar un marcador de posición para que ocupe el espacio del elemento <video> hasta que se cargue de forma diferida el video. Al igual que con los ejemplos de carga diferida de <img>, guarda la URL del video en el atributo data-src de cada elemento <source>. Desde allí, usa un código JavaScript similar a los ejemplos de carga diferida de imágenes basada en Intersection Observer:

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);
    });
  }
});

Cuando cargas de forma diferida un elemento <video>, debes iterar por todos los elementos <source> secundarios y cambiar sus atributos data-src a atributos src. Una vez que lo hagas, deberás activar la carga del video llamando al método load del elemento. Luego, el contenido multimedia comenzará a reproducirse automáticamente según el atributo autoplay.

Con este método, tienes una solución de video que emula el comportamiento de los GIF animados, pero no genera el mismo uso intensivo de datos que los GIF animados y puedes cargar de forma diferida ese contenido.

Carga diferida de bibliotecas

Las siguientes bibliotecas pueden ayudarte a cargar videos de forma diferida:

  • vanilla-lazyload y lozad.js son opciones súper ligeras que solo usan Intersection Observer. Por lo tanto, tienen un alto rendimiento, pero requieren polyfill para poder usarlas en navegadores anteriores.
  • yall.js es una biblioteca que usa Intersection Observer y se revierte a los controladores de eventos. También puede cargar de forma diferida imágenes poster de video con un atributo data-poster.
  • Si necesitas una biblioteca de carga diferida específica para React, puedes considerarreact-lazyload. Si bien no usa Intersection Observer, proporciona un método conocido de carga diferida de imágenes para los usuarios habituados a desarrollar aplicaciones con React.

Cada una de estas bibliotecas de carga diferida está bien documentada, con muchos patrones de marcado para diversos esfuerzos de carga diferida.