A menudo, las imágenes son el recurso más pesado y más común en la Web. Como resultado, optimizar las imágenes puede mejorar significativamente el rendimiento de tu sitio web. En la mayoría de los casos, optimizar las imágenes significa reducir el tiempo de red enviando menos bytes, pero también puedes optimizar la cantidad de bytes que se envían al usuario a través de la entrega de imágenes con el tamaño adecuado para el dispositivo del usuario.
Las imágenes se pueden agregar a una página con los elementos <img>
o <picture>
, o con la propiedad background-image
de CSS.
Tamaño de la imagen
La primera optimización que puedes realizar cuando se trata de usar recursos de imagen es mostrar la imagen con el tamaño correcto. En este caso, el término tamaño hace referencia a las dimensiones de una imagen. Sin considerar otras variables, una imagen que se muestra en un contenedor de 500 píxeles por 500 píxeles tendría un tamaño óptimo de 500 píxeles por 500 píxeles. Por ejemplo, usar una imagen cuadrada de 1,000 píxeles significa que la imagen es el doble de grande de lo necesario.
Sin embargo, hay muchas variables involucradas en la elección del tamaño de imagen adecuado, lo que hace que la tarea de elegir el tamaño de imagen adecuado en cada caso sea bastante complicada. En 2010, cuando se lanzó el iPhone 4, la resolución de la pantalla (640 x 960) era el doble que la del iPhone 3 (320 x 480). Sin embargo, el tamaño físico de la pantalla del iPhone 4 siguió siendo aproximadamente el mismo que el del iPhone 3.
Si se mostrara todo con la resolución más alta, el texto y las imágenes serían mucho más pequeños, la mitad de su tamaño anterior, para ser exactos. En cambio, 1 píxel se convirtió en 2 píxeles del dispositivo. Esto se denomina proporción de píxeles del dispositivo (DPR). El iPhone 4 y muchos modelos de iPhone lanzados después tenían un DPR de 2.
Si retomamos el ejemplo anterior, si el dispositivo tiene un DPR de 2 y la imagen se muestra en un contenedor de 500 píxeles por 500 píxeles, una imagen cuadrada de 1,000 píxeles (denominada tamaño intrínseco) ahora es el tamaño óptimo. Del mismo modo, si el dispositivo tiene un DPR de 3, el tamaño óptimo sería una imagen cuadrada de 1,500 píxeles.
srcset
El elemento <img>
admite el atributo srcset
, que te permite especificar una lista de posibles fuentes de imágenes que puede usar el navegador. Cada fuente de imagen especificada debe incluir la URL de la imagen y un descriptor de densidad de píxeles o de ancho.
<img
alt="An image"
width="500"
height="500"
src="/image-500.jpg"
srcset="/image-500.jpg 1x, /image-1000.jpg 2x, /image-1500.jpg 3x"
>
El fragmento de HTML anterior usa el descriptor de densidad de píxeles para sugerirle al navegador que use image-500.png
en dispositivos con una DPR de 1, image-1000.jpg
en dispositivos con una DPR de 2 y image-1500.jpg
en dispositivos con una DPR de 3.
Si bien todo esto puede parecer muy claro, el DPR de una pantalla no es la única consideración a la hora de elegir la imagen óptima para una página determinada. El diseño de la página es otra consideración.
sizes
La solución anterior solo funciona si muestras la imagen con el mismo tamaño de píxeles CSS en todos los viewports. En muchos casos, el diseño de una página (y el tamaño del contenedor con él) cambia según el dispositivo del usuario.
El atributo sizes
te permite especificar un conjunto de tamaños de fuente, en los que cada tamaño de fuente consta de una condición de contenido multimedia y un valor. El atributo sizes
describe el tamaño de visualización previsto de la imagen en píxeles de CSS. Cuando se combinan con los descriptores de ancho srcset
, el navegador puede elegir qué fuente de imagen es mejor para el dispositivo del usuario.
<img
alt="An image"
width="500"
height="500"
src="/image-500.jpg"
srcset="/image-500.jpg 500w, /image-1000.jpg 1000w, /image-1500.jpg 1500w"
sizes="(min-width: 768px) 500px, 100vw"
>
En el fragmento de HTML anterior, el atributo srcset
especifica una lista de imágenes candidatas entre las que puede elegir el navegador, separadas por comas. Cada candidato de la lista consta de la URL de la imagen, seguida de una sintaxis que denota el ancho intrínseco de la imagen. El tamaño intrínseco de una imagen son sus dimensiones. Por ejemplo, un descriptor de 1000w
indica que el ancho intrínseco de la imagen es de 1,000 píxeles.
Con esta información, el navegador evalúa la condición de los medios en el atributo sizes
y, en este caso, se le indica que, si el ancho del viewport del dispositivo supera los 768 píxeles, la imagen se mostrará con un ancho de 500 píxeles. En dispositivos más pequeños, la imagen se muestra en 100vw
, o el ancho completo de la vista del viewport.
Luego, el navegador puede combinar esta información con la lista de fuentes de imágenes srcset
para encontrar la imagen óptima. Por ejemplo, si el usuario está en un dispositivo móvil con un ancho de pantalla de 320 píxeles y un DPR de 3, la imagen se muestra en 320 CSS pixels x 3 DPR = 960 device pixels
. En este ejemplo, la imagen de tamaño más cercano sería image-1000.jpg
, que tiene un ancho intrínseco de 1,000 píxeles (1000w
).
Formatos de archivo
Los navegadores admiten varios formatos de archivo de imagen diferentes. Los formatos de imagen modernos, como WebP y AVIF, pueden proporcionar una mejor compresión que PNG o JPEG, lo que reduce el tamaño del archivo de imagen y, por lo tanto, tarda menos tiempo en descargarse. Si publicas imágenes en formatos modernos, puedes reducir el tiempo de carga de un recurso, lo que puede generar un procesamiento de imagen con contenido más grande (LCP) más bajo.
WebP es un formato ampliamente compatible que funciona en todos los navegadores modernos. WebP suele tener una mejor compresión que JPEG, PNG o GIF, y ofrece compresión con pérdida y sin pérdida. WebP también admite la transparencia del canal alfa, incluso cuando se usa compresión con pérdida, una función que no ofrece el códec JPEG.
AVIF es un formato de imagen más nuevo y, si bien no es tan compatible como WebP, sí disfruta de una compatibilidad bastante decente en todos los navegadores. AVIF admite compresión con pérdida y sin pérdida, y las pruebas mostraron ahorros superiores al 50% en comparación con JPEG en algunos casos. El AVIF también ofrece funciones de gama de colores amplia (WCG) y alto rango dinámico (HDR).
Compresión
En el caso de las imágenes, existen dos tipos de compresión:
La compresión con pérdida funciona reduciendo la precisión de la imagen a través de la cuantización, y se puede descartar información de color adicional con el submuestreo de crominancia. La compresión con pérdida es más eficaz en imágenes de alta densidad con mucho ruido y colores, por lo general, fotos o imágenes con contenido similar. Esto se debe a que es mucho menos probable que se noten los artefactos que produce la compresión con pérdida en imágenes tan detalladas. Sin embargo, la compresión con pérdida puede ser menos eficaz con imágenes que contienen bordes nítidos, como dibujos lineales, detalles similares o texto. La compresión con pérdida se puede aplicar a imágenes JPEG, WebP y AVIF.
La compresión sin pérdida reduce el tamaño del archivo comprimiendo una imagen sin pérdida de datos. La compresión sin pérdida describe un píxel en función de la diferencia con sus píxeles vecinos. La compresión sin pérdida se usa para los formatos de imagen GIF, PNG, WebP y AVIF.
Puedes comprimir tus imágenes con Squoosh, ImageOptim o un servicio de optimización de imágenes. Cuando se comprime, no hay una configuración universal adecuada para todos los casos. El enfoque recomendado sería experimentar con diferentes niveles de compresión hasta encontrar un buen equilibrio entre la calidad de la imagen y el tamaño del archivo. Algunos servicios avanzados de optimización de imágenes pueden hacerlo por ti automáticamente, pero es posible que no sean viables económicamente para todos los usuarios.
El elemento <picture>
El elemento <picture>
te brinda mayor flexibilidad para especificar varias imágenes candidatas:
<picture>
<source type="image/avif" srcset="image.avif">
<source type="image/webp" srcset="image.webp">
<img
alt="An image"
width="500"
height="500"
src="/image.jpg"
>
</picture>
Cuando usas elementos <source>
dentro del elemento <picture>
, puedes agregar compatibilidad con imágenes AVIF y WebP, pero recurrir a formatos de imagen heredados más compatibles si el navegador no admite formatos modernos. Con este enfoque, el navegador elige el primer elemento <source>
especificado que coincide. Si puede renderizar la imagen en ese formato, la usará. De lo contrario, el navegador pasa al siguiente elemento <source>
especificado. En el fragmento de HTML anterior, el formato AVIF tiene prioridad sobre el formato WebP y, si no se admite ninguno de los dos, se usa el formato JPEG.
Un elemento <picture>
requiere un elemento <img>
anidado dentro de él. Los atributos alt
, width
y height
se definen en <img>
y se usan independientemente de qué <source>
se seleccione.
El elemento <source>
también admite los atributos media
, srcset
y sizes
. Al igual que en el ejemplo de <img>
anterior, estos le indican al navegador qué imagen seleccionar en diferentes viewports.
<picture>
<source
media="(min-resolution: 1.5x)"
srcset="/image-1000.jpg 1000w, /image-1500.jpg 1500w"
sizes="(min-width: 768px) 500px, 100vw"
>
<img
alt="An image"
width="500"
height="500"
src="/image-500.jpg"
>
</picture>
El atributo media
toma una condición de contenido multimedia. En el ejemplo anterior, se usa la DPR del dispositivo como condición multimedia. Cualquier dispositivo con un DPR mayor o igual a 1.5 usaría el primer elemento <source>
. El elemento <source>
le indica al navegador que, en dispositivos con un viewport más ancho que 768 píxeles, la imagen candidata seleccionada se muestra con un ancho de 500 píxeles. En dispositivos más pequeños, ocupa todo el ancho del viewport. Si combinas los atributos media
y srcset
, puedes tener un control más preciso sobre qué imagen usar.
Esto se ilustra en la siguiente tabla, en la que se evalúan varias relaciones de píxeles del dispositivo y anchos de viewport:
Ancho del viewport (en píxeles) | 1 DPR | 1.5 DPR | 2 DPR | 3 DPR |
---|---|---|---|---|
320 | 500.jpg | 500.jpg | 500.jpg | 1000.jpg |
480 | 500.jpg | 500.jpg | 1000.jpg | 1500.jpg |
560 | 500.jpg | 1000.jpg | 1000.jpg | 1500.jpg |
1024 | 500.jpg | 1000.jpg | 1000.jpg | 1500.jpg |
1920 | 500.jpg | 1000.jpg | 1000.jpg | 1500.jpg |
Los dispositivos con un DPR de 1 descargan la imagen image-500.jpg
, incluida la mayoría de los usuarios de computadoras de escritorio, que ven la imagen en un tamaño extrínseco de 500 píxeles de ancho. Por otro lado, los usuarios de dispositivos móviles con un DPR de 3 descargan una image-1500.jpg
potencialmente más grande, la misma imagen que se usa en dispositivos de escritorio con un DPR de 3.
<picture>
<source
media="(min-width: 561px) and (min-resolution: 1.5x)"
srcset="/image-1000.jpg 1000w, /image-1500.jpg 1500w"
sizes="(min-width: 768px) 500px, 100vw"
>
<source
media="(max-width: 560px) and (min-resolution: 1.5x)"
srcset="/image-1000-sm.jpg 1000w, /image-1500-sm.jpg 1500w"
sizes="(min-width: 768px) 500px, 100vw"
>
<img
alt="An image"
width="500"
height="500"
src="/image-500.jpg"
>
</picture>
En este ejemplo, el elemento <picture>
se ajusta para incluir un elemento <source>
adicional que use imágenes diferentes para dispositivos anchos con una DPR alta:
Ancho del viewport (en píxeles) | 1 DPR | 1.5 DPR | 2 DPR | 3 DPR |
---|---|---|---|---|
320 | 500.jpg | 500.jpg | 1000-sm.jpg | 1000-sm.jpg |
480 | 500.jpg | 500.jpg | 1000-sm.jpg | 1500-sm.jpg |
560 | 500.jpg | 1000-sm.jpg | 1000-sm.jpg | 1500-sm.jpg |
1024 | 500.jpg | 1000.jpg | 1000.jpg | 1500.jpg |
1920 | 500.jpg | 1000.jpg | 1000.jpg | 1500.jpg |
Con esta consulta adicional, puedes ver que image-1000-sm.jpg
y image-1500-sm.jpg
se muestran en viewports pequeños. Esta información adicional te permite comprimir las imágenes aún más, ya que los artefactos de compresión no son muy visibles en ese tamaño y densidad, y tampoco se compromete la calidad de la imagen en los dispositivos de escritorio.
Como alternativa, si ajustas los atributos srcset
y media
, puedes evitar que se entreguen imágenes grandes en viewports pequeñas:
<picture>
<source
media="(min-width: 561px)"
srcset="/image-500.jpg, /image-1000.jpg 2x, /image-1500.jpg 3x"
>
<source
media="(max-width: 560px)"
srcset="/image-500.jpg 1x, /image-1000.jpg 2x"
>
<img
alt="An image"
width="500"
height="500"
src="/image-500.jpg"
>
</picture>
En el fragmento de HTML anterior, se quitaron los descriptores de ancho en favor de los descriptores de proporción de píxeles del dispositivo. Las imágenes que se entregan en un dispositivo móvil se limitan a /image-500.jpg
o /image-1000.jpg
, incluso en dispositivos con un DPR de 3.
Cómo administrar la complejidad
Cuando trabajas con imágenes responsivas, puedes encontrarte con muchas variaciones de tamaño y formatos diferentes para cada imagen. En el ejemplo anterior, se usan las variaciones para cada tamaño, pero se excluyen AVIF y WebP. ¿Cuántas variantes deberías tener? Al igual que con muchos problemas de ingeniería, la respuesta suele ser “depende”.
Si bien puede ser tentador tener tantas variaciones como sea posible para ofrecer la mejor opción, cada variante de imagen adicional tiene un costo y hace un uso menos eficiente de la caché del navegador. Con solo una variante, todos los usuarios reciben la misma imagen, por lo que se puede almacenar en caché de manera muy eficiente.
Por otro lado, si hay muchas variantes, cada una requiere otra entrada de caché. Los costos del servidor pueden aumentar y el rendimiento puede disminuir si la entrada de la caché de la variante venció y la imagen debe volver a recuperarse del servidor de origen.
Además, el tamaño de tu documento HTML crece con cada variación. Es posible que envíes varios kilobytes de HTML para cada imagen.
Publica imágenes según el encabezado de solicitud Accept
El encabezado de solicitud HTTP Accept
le informa al servidor qué tipos de contenido comprende el navegador del usuario. Tu servidor puede usar esta información para entregar el formato de imagen óptimo sin agregar bytes adicionales a tus respuestas HTML.
if (request.headers.accept) {
if (request.headers.accept.includes('image/avif')) {
return reply.from('image.avif');
} else if (request.headers.accept.includes('image/webp')) {
return reply.from('image.webp');
}
}
return reply.from('image.jpg');
El fragmento de HTML anterior es una versión simplificada del código que puedes agregar al backend de JavaScript de tu servidor para elegir y publicar el formato de imagen óptimo.
Si el encabezado Accept
de la solicitud incluye image/avif
, se entrega la imagen AVIF. De lo contrario, si el encabezado Accept
incluye image/webp
, se entrega la imagen WebP. Si no se cumple ninguna de estas condiciones, se entrega la imagen JPEG.
Puedes modificar las respuestas según el contenido del encabezado de solicitud Accept
en casi todos los tipos de servidores web. Por ejemplo, puedes reescribir las solicitudes de imágenes en servidores Apache según el encabezado Accept
con mod_rewrite
.
Este comportamiento no es muy diferente del que encontrarías en una red de distribución de contenido (CDN) de imágenes. Las CDN de imágenes son excelentes soluciones para optimizar las imágenes y enviar el formato óptimo según el dispositivo y el navegador del usuario.
La clave es encontrar un equilibrio, generar una cantidad razonable de imágenes candidatas y medir el impacto en la experiencia del usuario. Las diferentes imágenes pueden brindar resultados diferentes, y las optimizaciones aplicadas a cada una dependen de su tamaño dentro de la página y de los dispositivos que usan tus usuarios. Por ejemplo, una imagen hero de pantalla completa puede requerir más variantes que las imágenes en miniatura en una página de ficha de producto de comercio electrónico.
Carga diferida
Es posible indicarle al navegador que cargue imágenes de forma diferida cuando aparezcan en el viewport con el atributo loading
. Un valor de atributo de lazy
le indica al navegador que no descargue la imagen hasta que esté en (o cerca de) el viewport. Esto ahorra ancho de banda, lo que permite que el navegador priorice los recursos que necesita para renderizar el contenido crítico que ya está en el viewport.
decoding
El atributo decoding
le indica al navegador cómo debe decodificar la imagen. Un valor de async
le indica al navegador que la imagen se puede decodificar de forma asíncrona, lo que podría mejorar el tiempo de renderización de otro contenido. Un valor de sync
le indica al navegador que la imagen se debe presentar al mismo tiempo que otro contenido.
El valor predeterminado de auto
permite que el navegador decida qué es mejor para el usuario.
Demostraciones de imágenes
Ponga a prueba sus conocimientos
¿Qué formatos de imagen admiten la compresión sin pérdidas?
¿Qué formatos de imagen admiten la compresión con pérdida?
¿Qué le indica el descriptor de ancho (por ejemplo, 1000w
) al navegador sobre una imagen candidata especificada en un atributo srcset
?
¿Qué le indica el atributo sizes
al navegador sobre un elemento <img>
al que se aplica?
srcset
de un elemento <img>
se debe cargar, dadas las dimensiones del viewport actual del usuario.
srcset
del elemento <img>
.
A continuación: Rendimiento del video
Si bien las imágenes pueden ser el tipo de contenido multimedia más frecuente que se usa en la Web, están lejos de ser el único que debes tener en cuenta en términos de rendimiento. El video es otro tipo de contenido multimedia común que se usa en la Web y tiene sus propias consideraciones de rendimiento. En el siguiente módulo de este curso, exploraremos algunas técnicas para optimizar los videos y cargarlos de manera eficiente.