Novedades de la directiva NgOptimizedImage de Angular

Alex Castle
Alex Castle

Hace poco más de un año, el equipo de Chrome Aurora lanzó la directiva NgOptimizedImage de Angular. La directiva se enfoca principalmente en mejorar el rendimiento, según lo miden las métricas de las Métricas web esenciales. Combina optimizaciones de imágenes comunes y prácticas recomendadas en una API para el usuario que no es mucho más complicada que un elemento <img> estándar.

En 2023, mejoramos la directive con funciones nuevas. En esta publicación, se describe la más sustancial de esas funciones nuevas, con énfasis en por qué elegimos priorizar cada función y cómo puede ayudar a mejorar el rendimiento de las aplicaciones de Angular.

Nuevas funciones

NgOptimizedImage mejoró considerablemente con el tiempo, incluidas las siguientes funciones nuevas.

Modo de relleno

Proporcionar un atributo width y height para cambiar el tamaño de tus imágenes es una optimización extremadamente importante para reducir el desplazamiento del diseño, ya que los navegadores necesitan conocer la relación de aspecto de la imagen para ahorrar espacio para ella. Sin embargo, ajustar el tamaño de las imágenes es un trabajo adicional para los desarrolladores de aplicaciones y no tiene sentido en algunos casos de uso de imágenes.

La primera función principal que se agregó al componente de imagen después de la vista previa para desarrolladores ayuda a resolver esta tensión: el modo de relleno. De esta forma, los desarrolladores pueden incluir imágenes sin cambiarles el tamaño explícitamente y sin incurrir en un cambio de diseño.

Con el modo de relleno, se inhabilita el requisito de tamaño de la imagen y se le aplica un diseño automáticamente para que ocupe todo el elemento que la contiene. Esto separa la relación de aspecto de una imagen del espacio que ocupa en la página y te brinda un mayor control sobre cómo se ajustan las imágenes al diseño de tu página.

El modo de relleno usa NgOptimizedImage como una alternativa de mejor rendimiento a la propiedad background-image de CSS. Coloca una imagen dentro de <div> o algún otro elemento que tendría el diseño de background-image y, luego, habilita el modo de relleno, como se muestra en el ejemplo de código anterior. Usa las propiedades CSS object-fit y object-position en <div> para controlar cómo se posiciona la imagen en segundo plano.

// Height and width are required
<img ngSrc="example.com" height="300" width="400">

// Unless you use fill mode!
<div style="width: 100vw; height: 50em; position: relative">
  <img ngSrc="example.com" fill>
</div>

Generación de Srcset

Una de las técnicas de optimización de imágenes más eficaces es el uso del atributo srcset para garantizar que se descarguen imágenes del tamaño adecuado para cualquier dispositivo que acceda a tu aplicación. Si usas srcset en tu app, puedes evitar desperdiciar ancho de banda y mejorar significativamente tu LCP Core Web Vital.

La desventaja del atributo srcset es que puede ser engorroso de implementar. Escribir los valores de srcset de forma manual significa agregar varias líneas de marcado a cada elemento de imagen de tu app, con varias URLs personalizadas para cada srcset. También debes decidir entre un conjunto de puntos de interrupción, lo cual es complicado, ya que pueden representar tanto las densidades de pantalla como los tamaños de viewport de los dispositivos comunes.

Por eso, agregar la generación automática de srcset a la directiva NgOptimizedImage fue un gran logro posterior al lanzamiento. Con esta incorporación, cualquier aplicación que utilice una CDN que admita el cambio de tamaño de imágenes puede obtener srcsets completa y personalizable, y se agregará automáticamente a cada imagen generada con la directiva NgOptimizedImage.

Incluimos una API simplificada para configurar la propiedad sizes, que se usa para garantizar que cada imagen obtenga el tipo correcto de srcset. Si no incluyes un atributo sizes, sabremos que la imagen debe tener un tamaño fijo y debe obtener un srcset dependiente de la densidad, como el siguiente:

<img src="www.example.com/image.png" srcset="www.example.com/image.png?w=400 1x, www.example.com/image.png?w=800 2x" >

Este tipo de srcset garantiza que las imágenes se muestren en un tamaño que tenga en cuenta la densidad de píxeles del dispositivo del usuario.

Por otro lado, si incluyes la propiedad sizes, NgOptimizedImage genera un srcset responsivo que incluye puntos de inflexión para muchos tamaños de dispositivos e imágenes comunes, con esta lista predeterminada de puntos de inflexión:

[16, 32, 48, 64, 96, 128, 256, 384, 640, 750, 828, 1080, 1200, 1920, 2048, 3840]

Generación de preconexión

Para mejorar el LCP, es importante reducir el tiempo que dedican los usuarios a descargar la imagen de LCP. En la sección anterior, viste cómo srcset puede ayudar a transferir archivos de imagen más pequeños, pero una optimización igualmente importante es iniciar la transferencia lo antes posible. Una forma de hacerlo es usar etiquetas link rel="preconnect" para iniciar la conexión con tu dominio de imagen.

Desde el principio, NgOptimizedImage te advirtió si no te conectabas previamente al dominio de tu imagen de LCP, pero la advertencia no es la solución ideal. Preferimos solucionar el problema por ti. Y eso es exactamente lo que hace NgOptimizedImage ahora, con la generación automática de preconexiones.

Para admitir esta función, usamos el análisis de código estático para intentar detectar dominios de imágenes en los cargadores de NgOptimizedImage y generar automáticamente etiquetas de vínculo de conexión previa para esos dominios. Es posible que aún haya casos en los que se requieran vínculos de conexión previa manual, pero para la mayoría de los usuarios, la conexión previa automática significa un paso menos necesario para obtener un buen rendimiento de la imagen.

Compatibilidad mejorada para cargadores personalizados

Un elemento clave de NgOptimizedImage es la arquitectura del cargador, que permite que la directiva genere automáticamente URLs adaptadas a la CDN de imágenes de la aplicación. Se incluye un conjunto de cargadores integrados para CDN de uso general. También proporcionamos el uso de cargadores personalizados, que te permiten integrar NgOptimizedImage con casi cualquier solución de alojamiento de imágenes.

En el lanzamiento, estos cargadores personalizados tenían un alcance limitado y solo podían leer el atributo width del elemento de imagen. En respuesta a los comentarios de los usuarios, agregamos compatibilidad con una estructura de datos loaderParams personalizable, que permite pasar datos arbitrarios del elemento de imagen al cargador personalizado. Con la expansión, los cargadores personalizados pueden ser tan simples o complejos como lo requiera la infraestructura de imágenes de una aplicación.

En el siguiente ejemplo, se muestra cómo un cargador personalizado simple podría usar la API de loaderParams para seleccionar entre dos dominios de imagen alternativos:

const myCustomLoader = (config: ImageLoaderConfig) => {
  if (config.loaderParams?.alternateDomain) {
    return `https://alternate.domain.com/images/${config.src}`
  }
  return `https://primary.domain.com/images/${config.src}`;
};

En la documentación de Angular, puedes encontrar un ejemplo de un cargador personalizado más complejo.

Guía expandida para el rendimiento de las imágenes

Hasta ahora, cada alerta de rendimiento de imagen que agregamos a Angular formaba parte de la directiva NgOptimizedImage. Si no usas la directiva en la app, no recibirás ninguna guía sobre los problemas de rendimiento de las imágenes.

En Angular 17, expandimos el alcance de la guía de rendimiento de las imágenes para incluir todas las apps de Angular. Ahora, si detectamos patrones de imágenes que sabemos que son errores que afectan el rendimiento, como la carga diferida de tu imagen de LCP o la descarga de un archivo demasiado grande para la página, te lo informaremos, incluso si no usas NgOptimizedImage.

El rendimiento de las imágenes es importante para todas las apps, y nos entusiasma seguir creando barreras de seguridad para ayudar a evitar errores comunes en las apps de Angular.

Mirando hacia el futuro

Ya estamos trabajando arduamente en el desarrollo del siguiente conjunto de funciones para NgOptimizedImage. Si bien el rendimiento de las imágenes sigue siendo nuestra preocupación central, también nos gustaría agregar funciones que mejoren la experiencia de los desarrolladores para asegurarnos de que NgOptimizedImage siga siendo una opción atractiva para incluir imágenes en aplicaciones de Angular.

Una función que es una prioridad para nosotros son los marcadores de posición de imagen. Por lo general, se usan para que la carga de imágenes se vea mejor en las aplicaciones web, pero pueden perjudicar el rendimiento si se implementan de forma incorrecta. Esperamos compilar en NgOptimizedImage un sistema de marcadores de posición de imagen que priorice el rendimiento. No te pierdas los anuncios en nuestro blog.