Lazy loading de imágenes: 5 métodos para acelerar tu web

Lazy loading de imágenes: 5 métodos para acelerar tu web

Las imágenes son uno de los tipos de contenido más presentes en la web, así que el tiempo de carga de las páginas se convierte en un problema con mucha facilidad.

Incluso bien optimizadas, las imágenes pueden pesar bastante. Eso alarga la espera de los visitantes antes de poder acceder al contenido de tu sitio. Lo más probable es que se impacienten y se vayan a otra parte, salvo que encuentres una forma de cargar las imágenes que no interfiera con la velocidad percibida.

En este artículo verás cinco enfoques de carga diferida de imágenes que puedes añadir a tu caja de herramientas de optimización web para mejorar la experiencia de usuario en tu sitio.

¿Qué es el lazy loading?

La carga diferida de imágenes consiste en cargarlas de forma asíncrona, es decir, después de que el contenido principal se haya cargado del todo, o incluso de forma condicional, solo cuando aparecen en la ventana del navegador. Eso significa que si los usuarios no bajan hasta el final de la página, las imágenes situadas abajo ni siquiera llegarán a cargarse.

Bastantes sitios usan este enfoque, pero se nota sobre todo en los que van cargados de imágenes. Prueba a recorrer tu coto de caza habitual de fotos en alta resolución y verás enseguida que la web solo carga un número limitado de imágenes. A medida que bajas, los espacios reservados se van rellenando con las imágenes reales de la vista previa. Fíjate por ejemplo en la carga de Unsplash.com: bajar hasta esa parte de la página dispara la sustitución del marcador de posición por la foto a plena resolución.

¿Por qué te interesa la carga diferida de imágenes?

Hay al menos dos buenas razones para plantearte usar imágenes con carga diferida en tu sitio:

  • Si tu sitio usa JavaScript para mostrar contenido o para ofrecer cualquier funcionalidad a los usuarios, la carga del DOM se vuelve crítica muy rápido. Es habitual que los scripts esperen a que el DOM esté completamente cargado antes de empezar a ejecutarse. En un sitio con muchas imágenes, la carga diferida, o carga asíncrona de las imágenes, puede marcar la diferencia entre que los usuarios se queden o abandonen.
  • Como la mayoría de las soluciones de carga diferida funcionan cargando las imágenes solo si el usuario ha bajado hasta el punto en el que serían visibles en el viewport, esas imágenes no se cargarán nunca si el usuario no llega hasta ahí. Eso supone un ahorro considerable de ancho de banda, algo que la mayoría de los usuarios te agradecerá, sobre todo los que navegan desde el móvil y con conexiones lentas.

La carga diferida de imágenes ayuda al rendimiento de un sitio, pero ¿cuál es la mejor manera de aplicarla?

No existe el método perfecto.

Si vives y respiras JavaScript, montar tu propia solución de carga diferida no debería suponerte ningún problema. Nada te da más control que escribir el código tú mismo.

También puedes recorrer la web en busca de enfoques viables y empezar a probarlos. Es lo que hice yo, y así descubrí estas cinco técnicas interesantes.

#1 Carga diferida nativa

La carga diferida nativa de imágenes e iframes es una maravilla. Nada podría ser más sencillo que el marcado de abajo:

markup
<img src="myimage.jpg" loading="lazy" alt="..." />
<iframe src="content.html" loading="lazy"></iframe>

Como ves, no hay JavaScript ni intercambio dinámico del src de la imagen o del iframe: solo HTML de toda la vida.

El atributo loading nos permite retrasar las imágenes y los iframes que quedan fuera de pantalla hasta que el usuario baja hasta su posición en la página. loading admite uno de estos tres valores:

  • lazy: funciona de maravilla para la carga diferida
  • eager: pide al navegador que cargue de inmediato el contenido indicado.
  • auto: deja en manos del navegador la decisión de diferir la carga o no.

Este método no tiene rival: no añade sobrecarga, es limpio y es sencillo. Sin embargo, aunque a día de hoy la mayoría de los navegadores principales tienen buen soporte para el atributo loading, todavía no se han subido todos al carro.

#2 Carga diferida con la API Intersection Observer

La API Intersection Observer es una interfaz moderna que puedes usar para diferir la carga de imágenes y de otros contenidos.

Así presenta MDN esta API:

La API Intersection Observer permite observar de forma asíncrona los cambios en la intersección de un elemento objetivo con un elemento ancestro o con el viewport de un documento de nivel superior.

Dicho de otro modo, lo que se observa de forma asíncrona es la intersección de un elemento con otro.

Imagina que quieres diferir la carga de una galería de imágenes. El marcado de cada imagen se parecería a esto:

markup
<img data-src="image.jpg" alt="test image">

Fíjate en que la ruta de la imagen va en el atributo data-src y no en el atributo src. El motivo es que usar src haría que la imagen se cargase de inmediato, que es justo lo que no quieres.

En el CSS das a cada imagen un min-height, digamos 100px. Así cada hueco de imagen (el elemento img sin el atributo src) tiene una dimensión vertical:

css
img {
min-height: 100px;
/* más estilos aquí */
}

En el archivo JavaScript creas después un objeto config y lo registras en una instancia de intersectionObserver:

javascript
// crear un objeto de configuración: rootMargin y threshold
// son dos propiedades expuestas por la interfaz
const config = {
  rootMargin: '0px 0px 50px 0px',
  threshold: 0
};

// registrar el objeto config en una instancia
// de intersectionObserver
let observer = new intersectionObserver(function(entries, self) {
// iterar sobre cada entrada
  entries.forEach(entry {
    // procesar solo las imágenes que se cruzan.
    // isIntersecting es una propiedad expuesta por la interfaz
if(entry.isIntersecting) {
    /. / Fonction personnalisée qui copie le chemin vers l'image
      // de data-src a src
preloadImage(entry.target);
      // la imagen ya está en su sitio, deja de observar
      self.unobserve(entry.target);
}
});
}, config);

Por último, recorres todas tus imágenes y las añades a esa instancia de iterationObserver:

javascript
const imgs = document.querySelectorAll('[data-src]');
imgs.forEach(img {
  observer.observe(img);
});

Las ventajas de esta solución: es fácil de implementar, es eficiente y tiene la ventaja de dejar que intersectionObserver se encargue del trabajo pesado de los cálculos.

Por otro lado, aunque la API Intersection Observer está soportada por la mayoría de los navegadores en sus últimas versiones, no está soportada de forma sistemática por todos. Por suerte, hay un polyfill disponible.

#3 Lozad.js – Lazyload js

Una alternativa rápida y cómoda para implementar la carga diferida de imágenes es dejar que una biblioteca JS haga el trabajo pesado por ti.

Lozad es un cargador diferido muy eficiente, ligero y configurable, escrito en JavaScript puro y sin ninguna dependencia. Puedes usarlo para diferir la carga de imágenes, vídeos, iframes y bastante más, y se apoya en la API Intersection Observer.

Puedes instalar Lozad con npm o Yarn e importarlo con el bundler que prefieras:

bash
npm install --save lozad

yarn add lozad
javascript
import lozad from 'lozad';

También puedes cargar la biblioteca desde un CDN y añadirla al final de la página HTML en una etiqueta < script>:

markup
<script src="https://cdn.jsdelivr.net/npm/lozad/dist/lozad.min.js"></script>

Después, para una implementación básica, añade la clase lozad al recurso en tu marcado:

markup
<img class="lozad" data-src="img.jpg">

Por último, instancia Lozad en tu archivo JS:

javascript
const observer = lozad();
observer.observe();

Tienes todos los detalles de uso de la biblioteca en el repositorio GitHub de Lozad.

Si no te apetece meterte en el funcionamiento interno de la API Intersection Observer o simplemente buscas algo rápido que sirva para varios tipos de contenido, Lozad es una excelente opción.

Eso sí, vigila el soporte de los navegadores y, llegado el caso, acompaña esta biblioteca con un polyfill para la API Intersection Observer.

#4 Carga diferida con efecto de imagen borrosa

Lo primero que ves es una copia borrosa y de baja resolución de la imagen, mientras su versión en alta resolución se carga de forma diferida:

Imagen de marcador de posición borrosa en el sitio web de Medium
Imagen de marcador de posición borrosa en el sitio web de Medium
Imagen en alta resolución con carga diferida en el sitio web de Medium.
Imagen en alta resolución, cargada de forma diferida, en el sitio web de Medium.

Puedes diferir la carga de imágenes con este curioso efecto de desenfoque de varias formas.

Estas son todas las ventajas de la solución:

  • Rendimiento: solo 463 bytes de CSS y 1.007 bytes de JavaScript minificado.
  • Compatibilidad con pantallas retina
  • Sin dependencias: no hace falta jQuery ni ninguna otra biblioteca o framework.
  • Mejora progresiva para cubrir los navegadores antiguos y el JavaScript que falla.

Para conseguir este tipo de efecto, puedes descargar progressive-image.js.

#5 Yall.js – Lazyload js

Yall es un script de carga diferida muy completo para imágenes, vídeos e iframes. Se apoya en la API Intersection Observer y recurre con criterio a las técnicas tradicionales de gestión de eventos cuando hace falta.

Cuando incluyes Yall en tu documento, tienes que inicializarlo así:

javascript
<script src="yall.min.js"></script>
<script>
document.addEventListener("DOMContentLoaded", yall);
</script>

Después, para diferir la carga de una simple img, todo lo que necesitas en tu marcado es esto:

markup
<img class="lazy" src="placeholder.jpg" data-src="image-to-lazy-load.jpg" alt="Alternative text to describe image.">

Fíjate en lo siguiente:

  • añades la clase lazy al elemento
  • el valor de src es una imagen de marcador de posición
  • la ruta de la imagen cuya carga quieres diferir va en el atributo data-src

Entre las ventajas de Yall están estas:

  • gran rendimiento gracias a la API Intersection Observer
  • soporte de navegadores fantástico (llega hasta IE11)
  • ninguna otra dependencia necesaria

Para conocer todo lo que ofrece Yall y ver implementaciones más complejas, echa un vistazo a su página en GitHub.

Conclusión

Ahí lo tienes: cinco métodos de carga diferida de imágenes con los que puedes empezar a experimentar y a hacer pruebas en tus proyectos.

CSSHTML

Damien Flandrin Desarrollador web desde 2010, creador de Gekkode y de Email Impact. Cada artículo se prueba en un proyecto real antes de publicarse. Contacto
Newsletter

Las nuevas pruebas, tutoriales y proyectos, por correo.

Pruebas reproducibles, código versionado, resultados fechados. Nunca spam.