Novedades de la directiva NgOptimizedImage de Angular

Castillo de Alex
Castillo de Alex

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

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

Funciones nuevas

NgOptimizedImage ha mejorado sustancialmente a lo largo del tiempo, e incluye las siguientes funciones nuevas.

Modo de relleno

Ajustar el tamaño de tus imágenes proporcionando los atributos width y height es una optimización extremadamente importante para reducir el cambio de diseño, ya que los navegadores deben conocer la relación de aspecto de la imagen a fin de ahorrar espacio. Sin embargo, cambiar 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.

Ayudar a resolver esta tensión es la primera función importante que se agregó al componente de imagen posterior a la versión preliminar para desarrolladores: el modo de relleno. Esta es una forma en la que los desarrolladores pueden incluir imágenes sin explícitamente ajustar su tamaño 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 ajusta automáticamente el estilo de la imagen para llenar el elemento contenedor. 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 el modo en que las imágenes se ajustan al diseño de tu página.

El modo de relleno usa NgOptimizedImage como una alternativa con mejor rendimiento a la propiedad CSS de background-image. Coloca una imagen dentro de la <div> o de otro elemento que hubiera tenido el estilo background-image y, luego, habilita el modo de relleno, como se muestra en el ejemplo de código anterior. Usa las propiedades de 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 efectivas es el uso del atributo srcset para garantizar que se descarguen imágenes del tamaño adecuado en cualquier dispositivo que acceda a tu aplicación. El uso de srcset en toda tu app puede evitar que desperdicies ancho de banda y mejorar considerablemente tu LCP Core Web Vital.

La desventaja del atributo srcset es que puede ser difícil de implementar. Escribir manualmente los valores de srcset implica agregar varias líneas de lenguaje de marcado a cada elemento de imagen de tu app, con varias URLs personalizadas para cada srcset. También debes decidir 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 las viewports de los dispositivos comunes.

Es por eso que agregar la generación automatizada de srcset a la directiva NgOptimizedImage fue un hito importante después del lanzamiento. De esta forma, cualquier aplicación que use una CDN que admita el cambio de tamaño de imágenes puede agregar srcsets completas y personalizables a todas las imágenes generadas con la directiva NgOptimizedImage.

Se incluyó una API simplificada para configurar la propiedad sizes, que se usa para garantizar que cada imagen obtenga el tipo de srcset correcto. Si no incluyes un atributo sizes, sabemos que la imagen debe ser de tamaño fijo y debe obtener un srcset que depende 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 publiquen 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 interrupciones para muchos tamaños comunes de imágenes y dispositivos, utilizando esta lista predeterminada de puntos de interrupció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 los usuarios dedican a descargar la imagen de LCP. En la sección anterior, viste cómo srcset puede ayudar con la transferencia de archivos de imagen más pequeños, pero una optimización igualmente importante es comenzar la transferencia lo antes posible. Una forma de hacerlo es con las etiquetas link rel="preconnect" para iniciar la conexión al dominio de tu imagen.

Desde el principio, NgOptimizedImage te advierte si no te conectas previamente al dominio de tu imagen LCP, pero la advertencia no es la solución ideal: preferimos simplemente solucionar el problema por ti. Y eso es exactamente lo que hace NgOptimizedImage ahora, con la generación de preconexión automatizada.

Para admitir esta función, usamos análisis de código estático con el objetivo de intentar detectar dominios de imágenes en los cargadores de NgOptimizedImage y generar automáticamente etiquetas de vínculos de preconexión para esos dominios. Sin embargo, puede haber algunos casos en los que se requieran vínculos de preconexión manuales. Sin embargo, para la mayoría de los usuarios, la preconexión automática implica realizar un paso menos para lograr un buen rendimiento de la imagen.

Compatibilidad mejorada con cargadores personalizados

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

En el lanzamiento, estos cargadores personalizados tenían un alcance limitado y solo podían leer el atributo width desde el 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 imagen 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 imágenes 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}`;
};

Puedes encontrar un ejemplo de un cargador personalizado más complejo en la documentación de Angular.

Orientación ampliada sobre el rendimiento de las imágenes

Hasta ahora, todas las alertas de rendimiento de imágenes que agregamos a Angular formaban parte de la directiva de NgOptimizedImage. Si no usas la directiva en la app, no recibirás orientación sobre los problemas de rendimiento de las imágenes.

En Angular 17, ampliaremos el alcance de los lineamientos de rendimiento de imágenes para incluir todas las apps de Angular. Ahora, si detectamos patrones de imagen que sabemos que son errores que afectan el rendimiento, como la carga diferida de tu imagen LCP o la descarga de un archivo demasiado grande para la página, te avisaremos, 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 prevenir errores comunes en las apps de Angular.

Proyecciones para el futuro

Ya estamos trabajando en el desarrollo del próximo 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 de las funciones que más nos importa 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 afectar el rendimiento si se implementan de forma incorrecta. Esperamos compilar en NgOptimizedImage un sistema de marcadores de posición de imágenes que priorice el rendimiento. No te pierdas nuestro blog para enterarte de los anuncios.