Prácticas recomendadas sobre la carga diferida

Si bien la carga diferida de imágenes y videos ofrece beneficios de rendimiento positivos y medibles, no es una tarea para tomar a la ligera. Si te equivocas, podría haber consecuencias inesperadas. Por lo tanto, es importante tener en cuenta las siguientes consideraciones.

Presta atención a la línea de plegado

Puede ser tentador realizar una carga diferida en cada recurso multimedia de la página con JavaScript, pero debes resistir la tentación. Cualquier elemento que quede en la parte superior de la página no debería cargarse de forma diferida. Esos recursos deben considerarse recursos críticos y, por lo tanto, deben cargarse de forma normal.

La carga diferida retrasa la carga de recursos hasta que el DOM se vuelve interactivo, cuando las secuencias de comandos terminan de cargarse y comienzan a ejecutarse. Esto es correcto para las imágenes en la mitad inferior de la página, pero los recursos críticos en la mitad superior de la página deben cargarse con el elemento <img> estándar para que se muestren lo antes posible.

Por supuesto, hoy en día, la ubicación de la línea de plegado no es tan clara cuando los sitios web se ven en tantas pantallas de diferentes tamaños. Lo que se encuentra en la mitad superior de una laptop puede estar debajo en los dispositivos móviles. No hay un consejo infalible para abordar esto de manera óptima en cada situación. Deberás realizar un inventario de los elementos fundamentales de la página y cargar esas imágenes de manera típica.

Además, no es recomendable ser tan estricto con la línea de plegado como el umbral para activar la carga diferida. Puede ser más ideal para tus fines establecer una zona de búfer un poco más abajo de la mitad de la página para que las imágenes comiencen a cargarse mucho antes de que el usuario las desplace en el viewport. Por ejemplo, la API de Intersection Observer te permite especificar una propiedad rootMargin en un objeto de opciones cuando creas una nueva instancia de IntersectionObserver. Esto proporciona un búfer a los elementos, lo que activa el comportamiento de carga diferida antes de que el elemento esté en el viewport:

let lazyImageObserver = new IntersectionObserver(function(entries, observer) {
  // lazy-loading image code goes here
}, {
  rootMargin: "0px 0px 256px 0px"
});

Si el valor de rootMargin es similar a los valores que especificas para una propiedad margin de CSS, es porque lo es. En este caso, el margen inferior del elemento observado (el viewport del navegador de forma predeterminada, pero que se puede cambiar a un elemento específico con la propiedad root) se amplía en 256 píxeles. Esto significa que se ejecutará la función de devolución de llamada cuando un elemento de imagen esté dentro de los 256 píxeles del viewport y la imagen comience a cargarse antes de que el usuario la vea.

Para lograr el mismo efecto en navegadores que no admiten Intersection Observe, usa un código de control de eventos de desplazamiento y ajusta la verificación de getBoundingClientRect para incluir un búfer.

Cambio de diseño y marcadores de posición

La carga diferida de contenido multimedia puede generar cambios en el diseño si no se usan marcadores de posición. Estos cambios pueden desorientar a los usuarios y activar operaciones costosas de diseño del DOM que consumen recursos del sistema y contribuyen a los bloqueos. Como mínimo, considera usar un marcador de posición de color sólido que ocupe las mismas dimensiones que la imagen objetivo o técnicas como LQIP o SQIP que sugieran el contenido de un elemento multimedia antes de que se cargue.

Para las etiquetas <img>, src debería apuntar inicialmente a un marcador de posición hasta que ese atributo se actualice con la URL final de la imagen. Usa el atributo poster en un elemento <video> para apuntar a una imagen de marcador de posición. Además, usa los atributos width y height en las etiquetas <img> y <video>. Esto garantiza que la transición de los marcadores de posición a las imágenes finales no cambie el tamaño renderizado del elemento mientras se carga el contenido multimedia.

Demoras en la decodificación de imágenes

La carga de imágenes grandes en JavaScript y su colocación en el DOM pueden ocupar el subproceso principal, lo que provoca que la interfaz de usuario no responda durante un breve período mientras se produce la decodificación. La decodificación asíncrona de imágenes con el método decode antes de insertarlas en el DOM puede reducir este tipo de bloqueos, pero ten cuidado: todavía no está disponible en todas partes y agrega complejidad a la lógica de carga diferida. Si quieres usarlo, debes revisarlo. A continuación, se muestra cómo puedes usar Image.decode() con un resguardo:

var newImage = new Image();
newImage.src = "my-awesome-image.jpg";

if ("decode" in newImage) {
  // Fancy decoding logic
  newImage.decode().then(function() {
    imageContainer.appendChild(newImage);
  });
} else {
  // Regular image load
  imageContainer.appendChild(newImage);
}

Consulta este vínculo de CodePen para ver código similar a este ejemplo en acción. Si la mayoría de las imágenes son relativamente pequeñas, es posible que esto no sea de gran ayuda, pero sin dudas puede reducir los bloqueos cuando se cargan de forma diferida imágenes grandes y se insertan en el DOM.

Cuando no se cargan los elementos

A veces, los recursos multimedia no se cargan por una razón u otra, y se producen errores. ¿Cuándo podría suceder esto? Depende, pero aquí te mostramos una situación hipotética: tienes una política de almacenamiento en caché HTML durante un período breve (p.ej., cinco minutos) y el usuario visita el sitio o deja una pestaña inactiva abierta durante un período prolongado (p.ej., varias horas) y vuelve a leer el contenido. En algún punto de este proceso, se produce una reimplementación. Durante esta implementación, el nombre de un recurso de imagen cambia debido al control de versiones basado en hash o se quita por completo. Para cuando el usuario realiza una carga diferida de la imagen, el recurso ya no está disponible y, por lo tanto, falla.

Si bien estos casos son relativamente poco frecuentes, puede ser conveniente que tengas un plan de copia de seguridad si falla la carga diferida. Para las imágenes, una solución de este tipo puede ser similar a la siguiente:

var newImage = new Image();
newImage.src = "my-awesome-image.jpg";

newImage.onerror = function(){
  // Decide what to do on error
};
newImage.onload = function(){
  // Load the image
};

Lo que decidas hacer en caso de que se produzca un error dependerá de tu aplicación. Por ejemplo, puedes reemplazar el área del marcador de posición de la imagen por un botón que le permita al usuario intentar cargar la imagen de nuevo o, simplemente, mostrar un mensaje de error en el área del marcador de posición de la imagen.

También podrían surgir otras situaciones. Lo que sea que hagas, nunca es mala idea indicarle al usuario cuándo se produjo un error y, quizás, indicarle que realice una acción si algo sale mal.

Disponibilidad de JavaScript

No se debe suponer que JavaScript está siempre disponible. Si vas a cargar imágenes de forma diferida, considera ofrecer lenguaje de marcado <noscript> que muestre imágenes en caso de que JavaScript no esté disponible. El ejemplo de resguardo más simple posible implica usar elementos <noscript> para entregar imágenes si JavaScript está desactivado:

Soy una imagen.

Si JavaScript está desactivado, los usuarios verán tanto la imagen de marcador de posición como la imagen contenida en los elementos <noscript>. Para evitar esto, coloca una clase de no-js en la etiqueta <html> de la siguiente manera:

<html class="no-js">

Luego, coloca una línea de la secuencia de comandos intercalada en el elemento <head> antes de que se soliciten hojas de estilo a través de etiquetas <link> para quitar la clase no-js del elemento <html> si JavaScript está activado:

<script>document.documentElement.classList.remove("no-js");</script>

Por último, usa CSS para ocultar elementos con una clase diferida cuando JavaScript no está disponible:

.no-js .lazy {
  display: none;
}

Esto no evita que se carguen las imágenes de marcador de posición, pero el resultado es más conveniente. Las personas que tienen JavaScript desactivado obtienen algo más que imágenes de marcador de posición, lo que es mejor que usar marcadores de posición y no tener ningún contenido de imagen significativo.