La causa principal de la disminución del rendimiento de las aplicaciones web y la necesidad de Lazy Loading

En las aplicaciones web modernas, las imágenes son uno de los elementos clave que determinan la experiencia del usuario (UX) y el rendimiento web. Con el aumento en el uso de imágenes de alta resolución y el crecimiento del contenido de imágenes en la página, la desaceleración de la velocidad de carga de los sitios web se ha convertido en un problema inevitable. Cargar todas las imágenes de forma simultánea al inicio causa cuellos de botella de rendimiento, como los siguientes:

  • Aumento del tiempo de carga inicial (retraso de FCP y LCP): Al descargar imágenes fuera del viewport, se retrasa el momento en que los usuarios pueden ver realmente el contenido (First Contentful Paint) y el momento en que se carga el elemento de contenido más grande (Largest Contentful Paint). Esto también afecta negativamente a las métricas de Core Web Vitals.

  • Consumo innecesario de ancho de banda: Se desperdicia recursos de red en imágenes que los usuarios no verán al desplazarse, aumentando la carga de datos en entornos móviles y obstruyendo la carga de otros recursos importantes en entornos de escritorio debido a solicitudes de red innecesarias.

  • Aumento de la carga del servidor: Al haber múltiples solicitudes para todas las imágenes, la carga del servidor se incrementa, lo que puede llevar a una disminución de la estabilidad del servicio.

Una de las estrategias más efectivas para resolver estos problemas es el Lazy Loading de imágenes. Lazy Loading es una técnica que carga las imágenes de forma asíncrona cuando entran en el viewport del usuario o alcanzan un umbral, en lugar de cargarlas todas al inicio de la página.

Principios de funcionamiento y métodos de aplicación del Lazy Loading de imágenes

Los métodos principales para aplicar Lazy Loading se dividen en dos grandes categorías, y deben seleccionarse según las necesidades del proyecto entendiendo sus pros y contras.

1. Lazy Loading nativo del navegador (loading="lazy")

Es el método más simple y poderoso, que se puede implementar simplemente agregando el atributo loading="lazy" a las etiquetas HTML <img>. Es compatible con la mayoría de los navegadores modernos y maneja el Lazy Loading de forma optimizada por el navegador.

<img src="placeholder.jpg"
     data-src="actual-image.jpg"
     alt="Descripción de la imagen"
     loading="lazy"
     width="500"
     height="300">
  • Ventajas:

    • Facilidad de implementación: Se puede aplicar solo añadiendo atributos HTML sin necesidad de código JavaScript adicional.

    • Optimización del rendimiento: Como se implementa a nivel del motor del navegador, puede ser más eficiente y rápido que las soluciones basadas en JavaScript.

    • Menor carga para desarrolladores: El navegador se encarga del procesamiento sin gestionar APIs complejas de Intersection Observer o event listeners.

  • Desventajas:

    • Compatibilidad del navegador: No todos los navegadores antiguos pueden soportarlo completamente (sin embargo, la mayoría de los navegadores modernos sí lo hacen).

    • Falta de control fino: Es difícil para los desarrolladores controlar finamente los umbrales de carga o las estrategias de carga.

Recomendación: Si no hay requisitos especiales de control, es recomendable aplicar Lazy Loading nativo como opción preferente.

2. Lazy Loading basado en JavaScript (uso de la API Intersection Observer)

Cuando se requiere control más fino o es necesario un fallback para navegadores que no soportan el atributo loading="lazy", se puede implementar Lazy Loading basado en JavaScript. Anteriormente, se utilizaban listeners de eventos de desplazamiento, pero actualmente, debido a problemas de rendimiento, el uso de la API Intersection Observer se ha vuelto estándar.

Lógica de implementación básica:

  1. Inicialmente, se especifica una imagen de reemplazo en el atributo src o se almacena la URL de la imagen real en el atributo data-src.

  2. Se crea una instancia de Intersection Observer y se observan los elementos de imagen.

  3. Cuando un elemento de imagen entra en el viewport o alcanza un umbral definido, se ejecuta la función de callback del Observer.

  4. En la función de callback, se mueve la URL de la imagen real almacenada en data-src al atributo src para cargar la imagen.

  5. Una vez que la carga de la imagen se completa, se detiene la observación del elemento de imagen.

// HTML (ejemplo)
// <img class="lazyload" data-src="actual-image.jpg" alt="Descripción">

// JavaScript
document.addEventListener("DOMContentLoaded", function() {
    const lazyImages = [].slice.call(document.querySelectorAll("img.lazyload"));

    if ("IntersectionObserver" in window) {
        let lazyImageObserver = new IntersectionObserver(function(entries, observer) {
            entries.forEach(function(entry) {
                if (entry.isIntersecting) {
                    let lazyImage = entry.target;
                    lazyImage.src = lazyImage.dataset.src;
                    // lazyImage.srcset = lazyImage.dataset.srcset; // también si se necesita srcset
                    lazyImage.classList.remove("lazyload");
                    lazyImageObserver.unobserve(lazyImage);
                }
            });
        }, {
            // Margen raíz: área para cargar las imágenes anticipadamente desde el borde del viewport (px o %)
            // Ej: "0px 0px 200px 0px" carga 200px hacia abajo
            rootMargin: "0px 0px 200px 0px"
        });

        lazyImages.forEach(function(lazyImage) {
            lazyImageObserver.observe(lazyImage);
        });
    } else {
        // Fallback para navegadores que no soportan IntersectionObserver
        // (ej: usando listeners de eventos de scroll o cargando todas las imágenes inmediatamente)
        // Esta parte no se recomienda por razones de rendimiento, así que evalúa según los navegadores objetivo
        lazyImages.forEach(function(lazyImage) {
            lazyImage.src = lazyImage.dataset.src;
        });
    }
});
  • Bibliotecas populares de Lazy Loading en JavaScript:

    • lazysizes: Muy ligera, amigable con SEO, y soporta varios formatos de imagen, incluyendo srcset y picture. También puede ser usada como fallback para Lazy Loading nativo.

    • lozad.js: Se destaca por su pequeño tamaño y alto rendimiento.

Consideraciones y consejos de optimización al aplicar Lazy Loading

Lazy Loading es una poderosa técnica de optimización del rendimiento, pero aplicarla indiscriminadamente a todas las imágenes puede perjudicar la experiencia del usuario.

  1. Gestión de imágenes 'Above the Fold': No se debe aplicar Lazy Loading a imágenes que se ven inmediatamente en la parte superior de la pantalla (Above the Fold). Estas imágenes influyen directamente en el Largest Contentful Paint (LCP) de la página, por lo que deben cargarse inmediatamente mediante loading="eager" o ser excluidas de Lazy Loading.
<img src="logo.png" alt="Logo de la empresa" width="100" height="50" loading="eager">

Análisis de descomposición de LCP

  1. Especificar atributos width y height: Se deben especificar los atributos width y height en HTML para evitar el desplazamiento del diseño (Cumulative Layout Shift, CLS). Esto permite que el navegador reserve espacio para la imagen antes de que se cargue, evitando que el diseño se desplace durante la carga. Esto es esencial para mejorar las puntuaciones de Core Web Vitals.

  2. Usar imágenes de reemplazo: Para reducir el espacio visual en blanco que se muestra a los usuarios antes de que se cargue una imagen que se encuentra en Lazy Loading, es recomendable usar imágenes de baja resolución borrosas o un fondo de color sólido como Placeholder. Esto ayuda a indicar a los usuarios que la página se está cargando.

  3. Utilizar srcset y la etiqueta <picture>: Usar imágenes responsivas (srcset) y direcciones artísticas (picture) junto con Lazy Loading para proporcionar imágenes optimizadas para diferentes tamaños y resoluciones de pantalla, reduciendo aún más las descargas innecesarias de imágenes.

<picture>
    <source srcset="image-large.webp" type="image/webp" media="(min-width: 1200px)" loading="lazy">
    <source srcset="image-medium.webp" type="image/webp" media="(min-width: 768px)" loading="lazy">
    <img src="placeholder.jpg" data-src="image-small.jpg" alt="Descripción" loading="lazy">
</picture>

Puntuación de rendimiento de Lighthouse

Conclusión: Capturando dos pájaros de un tiro: Rendimiento y Experiencia del Usuario con Lazy Loading

El Lazy Loading de imágenes no solo retrasa la carga de imágenes, sino que es una técnica de optimización web esencial que mejora drásticamente el rendimiento de carga inicial de la página, optimiza el uso del ancho de banda y reduce la carga del servidor. Con la creciente importancia de métricas de rendimiento web como Core Web Vitals, la aplicación estratégica de Lazy Loading se ha vuelto un aspecto que todo desarrollador debe considerar.

Es recomendable considerar primero Lazy Loading nativo, pero en caso de necesitar control más fino o soportar entornos de navegador específicos, se pueden evaluar implementaciones basadas en JavaScript usando la API Intersección Observer o bibliotecas. Además, aplicar consejos de optimización como la gestión de imágenes 'Above the Fold', especificar width/height, y utilizar Placeholder pueden contribuir a ofrecer a los usuarios una experiencia web rápida y agradable.