Usar la carga de imágenes diferidas para mejorar el rendimiento de los proyectos web es una técnica de optimización popular. La cuestión es que las imágenes son recursos "pesados" que están llenos de sitios web modernos. Ya hemos publicado algo sobre esto.
Aquí puede leer acerca de lo que la carga diferida le dio al sitio web de Walmart y aprender a usar IntersectionObserver en los proyectos React.
Aquí hay un artículo sobre la optimización de sitios estáticos.
Aquí hay material reciente sobre la implementación de la carga diferida utilizando las herramientas del navegador.

Hoy traemos a su atención una traducción de un artículo en el que se describe el uso de la API IntersectionObserver utilizando una página web simple como ejemplo. Este material está destinado a programadores principiantes.
¿Qué es la carga lenta de imágenes?
Al analizar el rendimiento de la aplicación, dos indicadores se destacan: el tiempo hasta la primera interactividad (Tiempo para interactuar) y el consumo de recursos (Consumo de recursos). Inevitablemente tendrán que enfrentarse a quienes se dedican al desarrollo web. Además, pueden surgir problemas con las aplicaciones no solo porque tardan mucho tiempo en prepararse para el trabajo o porque consumen demasiados recursos. Pero, en cualquier caso, es muy importante encontrar las fuentes de estos problemas lo antes posible y esforzarse por garantizar que los problemas ni siquiera surjan.
El uso de tecnologías de carga de imágenes diferidas en aplicaciones web minimiza el riesgo de problemas de rendimiento. Es decir, si analizamos la carga diferida desde el punto de vista de su influencia en el momento de poner la página en condiciones de trabajo y el consumo de recursos, obtenemos lo siguiente:
- Tiempo para la primera interactividad. Este es el momento en que la aplicación web necesita cargarse y llevar la interfaz a un estado adecuado para que el usuario pueda trabajar con ella. La carga diferida (además, estamos hablando no solo de imágenes) optimiza el tiempo de respuesta de las aplicaciones gracias a la tecnología de separar el código y cargar solo lo que se necesita para una página en particular, o lo que se necesita en un momento específico en el tiempo.
- Consumo de recursos. Los humanos son seres impacientes. Si un sitio web necesita más de 3 segundos para cargarse, el 70% de los usuarios abandonan ese sitio. Las aplicaciones web no deberían cargar tanto tiempo. La carga diferida le permite reducir la cantidad de recursos necesarios para operar las páginas. Esto, por ejemplo, puede significar que el código de un determinado proyecto se divide en fragmentos que se cargan solo en las páginas que los necesitan. Como resultado, el rendimiento del sitio aumenta y el consumo de recursos del sistema disminuye.
Por lo tanto, las tecnologías de carga lenta aceleran las aplicaciones, reduciendo el tiempo que se descargan y abren. Esto se logra debido al hecho de que los recursos se cargan solo cuando se necesitan.
Aquí hay un par de ventajas que la carga diferida brinda a los proyectos web:
- Las páginas se cargan más rápido y se ponen en funcionamiento.
- En la carga inicial de páginas, debe transferir y procesar menos datos.
Imágenes de carga diferida utilizando IntersectionObserver API
Si piensa en cómo se cargan las páginas web, queda claro que no tiene sentido descargar imágenes u otros recursos que no son visibles para el usuario. Cuando se usa carga diferida, las imágenes que están en el área visible de la página se cargan primero. Luego, a medida que el usuario se desplaza por la página, se cargan otras imágenes que caen en el área visible de la página.
Considera un ejemplo.
Página y su área visibleEcha un vistazo a la figura anterior. Aquí puede ver el navegador y la página web cargados en él. Las imágenes
#IMG_1
y
#IMG_2
están en el alcance de la página. Esto significa que son visibles para el usuario y están dentro de los límites del área de la ventana del navegador que se muestra en la pantalla.
Este orden de trabajo con la página no puede considerarse ideal cuando, cuando se carga, las imágenes
#IMG_1
,
#IMG_2
,
#IMG_3
y
#IMG_4
. Solo
#IMG_1
y
#IMG_2
son visibles para el
#IMG_2
, y
#IMG_3
y
#IMG_4
están ocultos para él. Si carga la primera y la segunda imagen mientras carga la página, pero no descarga la tercera y cuarta imagen, esto podría tener un impacto positivo en el rendimiento del sitio. A saber, estamos hablando de lo siguiente. Cuando el usuario desplaza la página para que la tercera imagen sea visible, se carga. Si el desplazamiento continúa y la cuarta imagen se hace visible, también se carga.
Desplazarse por una página y cargar imágenesAhora que hemos descubierto exactamente cómo queremos trabajar con las imágenes, nos preguntamos cómo descubrir que cierto elemento de una página cae en su área visible. Los navegadores modernos tienen una API que le permite al programador saber cuándo un área determinada de la página se hace visible. Se trata de la API IntersectionObserver. Permite, mediante mecanismos asincrónicos, organizar el monitoreo de elementos y recibir notificaciones en los casos en que un elemento cruza el alcance del documento o cruza el borde de otro elemento.
Para configurar la carga diferida de imágenes, primero debemos preparar un código de plantilla para el elemento que se utilizará para describir las imágenes. Aquí esta:
<img class="lzy_img" src="lazy_img.jpg" data-src="real_img.jpg" />
La clase le permite identificar un elemento como una imagen a la que se aplicará la carga diferida. El atributo
src
permite mostrar una imagen de marcador de posición antes de mostrar la imagen real.
data-src
almacena la dirección de la imagen real, que se cargará cuando el elemento ingrese al área visible de la página.
Ahora escribamos el código que implementa la carga diferida. Como ya se mencionó, utilizaremos la API IntersectionObserver para detectar el momento en que la imagen ingresa al área de visualización de la página.
Primero, cree una instancia de
IntersectionObserver
:
const imageObserver = new IntersectionObserver(...);
El constructor
IntersectionObserver
acepta una función con dos parámetros. Uno de ellos almacena una matriz que consta de elementos que necesitan ser monitoreados, el otro una instancia de
IntersectionObserver
. Se ve así:
const imageObserver = new IntersectionObserver((entries, imgObserver) => { entries.forEach((entry) => {
El código de función verifica si las imágenes representadas por las
entries
matriz de
entries
cruzan con la ventana gráfica. Si este es el caso, entonces el atributo
src
de la imagen correspondiente registra lo que estaba en su atributo
data-src
.
const imageObserver = new IntersectionObserver((entries, imgObserver) => { entries.forEach((entry) => { if(entry.isIntersecting) { const lazyImage = entry.target lazyImage.src = lazyImage.dataset.src } }) });
Aquí verificamos, usando la condición
if(entry.isIntersecting) {...}
, si el elemento cruza el área de visualización del navegador. Si es así, guardamos el elemento
img
en la constante
lazyImage
. Luego escribimos en su atributo
src
lo que estaba en su atributo
data-src
. Gracias a esto, la imagen cuya dirección se almacena en
data-src
se carga y se muestra. En el navegador, parece reemplazar la imagen del marcador de posición,
lazy_img.jpg
, con una imagen real.
Ahora necesitamos usar el método
.observe()
de nuestra instancia
IntersectionObserver
para comenzar a monitorear los elementos que nos interesan:
imageObserver.observe(document.querySelectorAll('img.lzy_img'));
Aquí seleccionamos todos los elementos
img
con la clase
lzy_img
document.querySelectorAll('img.lzy_img')
con el comando
document.querySelectorAll('img.lzy_img')
y los pasamos al método
.observe()
. Habiendo recibido una lista de elementos, comienza el proceso de observarlos.
Para probar este ejemplo, comenzamos inicializando el proyecto Node.js:
mkdir lzy_img cd lzy_img npm init -y
Ahora cree el archivo
lzy_img
en la carpeta
lzy_img
:
touch index.html
Agregue el siguiente código:
<html> <title>Lazy Load Images</title> <body> <div> <div style=""> <img class="lzy_img" src="lazy_img.jpg" data-src="img_1.jpg" /> <hr /> </div> <div style=""> <img class="lzy_img" src="lazy_img.jpg" data-src="img_2.jpg" /> <hr /> </div> <div style=""> <img class="lzy_img" src="lazy_img.jpg" data-src="img_3.jpg" /> <hr /> </div> <div style=""> <img class="lzy_img" src="lazy_img.jpg" data-src="img_4.jpg" /> <hr /> </div> <div style=""> <img class="lzy_img" src="lazy_img.jpg" data-src="img_5.jpg" /> <hr /> </div> </div> <script> document.addEventListener("DOMContentLoaded", function() { const imageObserver = new IntersectionObserver((entries, imgObserver) => { entries.forEach((entry) => { if (entry.isIntersecting) { const lazyImage = entry.target console.log("lazy loading ", lazyImage) lazyImage.src = lazyImage.dataset.src } }) }); const arr = document.querySelectorAll('img.lzy_img') arr.forEach((v) => { imageObserver.observe(v); }) }) </script> </body> </html>
Puede notar que aquí se describen 5 imágenes, en las que se muestra el marcador de posición
lazy_img.jpg
al cargar. El atributo
data-src
de cada uno de ellos contiene información sobre imágenes reales. Aquí hay una lista de nombres de imágenes utilizados en el proyecto:
lazy_img.jpg img_1.jpg img_2.jpg img_3.jpg img_4.jpg img_5.jpg
Debe crear todas estas imágenes usted mismo llevando la carpeta del proyecto a la vista que se muestra en la siguiente figura.
Carpeta de proyectosEn mi caso, el archivo
lazy_img.jpg
preparó con Windows Paint y las imágenes reales (
img_*.jpg
Jpg ) se tomaron de
pixabay.com .
Tenga en cuenta que la devolución de llamada utilizada para crear la instancia de
IntersectionObserver
tiene una llamada
console.log()
. Esto nos permitirá aprender sobre cómo realizar operaciones para cargar diferentes imágenes.
Ahora, para servir
index.html
, usamos el paquete
http-server
:
npm i http-server
Agregue la propiedad de
start
a la sección de
scripts
del archivo
package.json
:
"scripts": { "start": "http-server ./" }
Después de eso, ejecute el
npm run start
en la terminal.
Ahora abra el navegador y vaya a la dirección
127.0.0.1:8080
. Nuestra página
index.html
al principio se parecerá a la siguiente figura.
Página sin imágenes realesPuede notar que donde deberían estar las imágenes reales, ahora solo se muestran marcadores de posición. Aquí procedemos de la suposición de que la primera imagen está en el área de visualización, por lo que el navegador comienza a descargarla. Como resultado, la página ahora se verá así.
Navegador subió la primera imagenOtras imágenes aún no cargadas. Todavía no han llegado al área de visualización.
Si se desplaza por la página mientras mira la consola, notará un problema. Consiste en el hecho de que al desplazarse por la misma imagen en la ventana de visualización, el navegador intenta descargarla varias veces.
Múltiples cargas de la misma imagen.Esto no es en absoluto lo que necesitamos. Esto conduce a un desperdicio innecesario de recursos del sistema y degrada el rendimiento del proyecto.
Para resolver este problema, necesitamos eliminar el elemento
img
, cuya imagen real ya está cargada, de la lista de elementos que está observando nuestra instancia de
IntersectionObserver
. También necesitamos eliminar la clase
lzy_img
de este elemento. Esto es lo que parece en el código de función de devolución de llamada editado:
<script> document.addEventListener("DOMContentLoaded", function() { const imageObserver = new IntersectionObserver((entries, imgObserver) => { entries.forEach((entry) => { if (entry.isIntersecting) { const lazyImage = entry.target console.log("lazy loading ", lazyImage) lazyImage.src = lazyImage.dataset.src lazyImage.classList.remove("lzy_img"); imgObserver.unobserve(lazyImage); } }) }); const arr = document.querySelectorAll('img.lzy_img') arr.forEach((v) => { imageObserver.observe(v); }) }) </script>
Después de cargar la imagen real del elemento
img
correspondiente, su clase
lzy_img
se
lzy_img
y el elemento se excluye de la lista de elementos que
IntersectionObserver
está observando.
Aquí está el código de muestra:
<html> <title>Lazy Load Images</title> <body> <div> <div style=""> <img class="lzy_img" src="lazy_img.jpg" data-src="img_1.jpg" /> <hr /> </div> <div style=""> <img class="lzy_img" src="lazy_img.jpg" data-src="img_2.jpg" /> <hr /> </div> <div style=""> <img class="lzy_img" src="lazy_img.jpg" data-src="img_3.jpg" /> <hr /> </div> <div style=""> <img class="lzy_img" src="lazy_img.jpg" data-src="img_4.jpg" /> <hr /> </div> <div style=""> <img class="lzy_img" src="lazy_img.jpg" data-src="img_5.jpg" /> <hr /> </div> </div> <script> document.addEventListener("DOMContentLoaded", function() { const imageObserver = new IntersectionObserver((entries, imgObserver) => { entries.forEach((entry) => { if (entry.isIntersecting) { const lazyImage = entry.target console.log("lazy loading ", lazyImage) lazyImage.src = lazyImage.dataset.src lazyImage.classList.remove("lzy_img"); imgObserver.unobserve(lazyImage); } }) }); const arr = document.querySelectorAll('img.lzy_img') arr.forEach((v) => { imageObserver.observe(v); }) }) </script> </body> </html>
Resumen
Examinamos el ejemplo más simple de la implementación del sistema de carga diferida de imágenes. Pero IntersectionObserver le permite crear esquemas de carga bastante complejos y perezosos. Si está interesado en este tema, le recomendamos que consulte las publicaciones mencionadas al comienzo del material y lea la
documentación .
Estimados lectores! ¿Tiene algún ejemplo de mejorar el rendimiento del sitio web después de implementar un sistema de carga de imágenes diferidas?
