Automatiza la compresión y la codificación

Haz que la generación de fuentes de imágenes de alto rendimiento sea una parte perfecta de tu proceso de desarrollo.

Todas las sintaxis de este curso, desde la codificación de datos de imágenes hasta el lenguaje de marcado denso en información que impulsa las imágenes receptivas, son métodos para que las máquinas se comuniquen con ellas. Descubriste varias formas en las que un navegador cliente puede comunicar sus necesidades a un servidor y a un servidor para responder de igual forma. El lenguaje de marcado de imágenes responsivas (en particular, srcset y sizes) logran describir una cantidad impactante de información en relativamente pocos caracteres. Para bien o peor, esa brevedad es por diseño: hacer que estas sintaxis sean menos secas y tan fáciles de analizar para los desarrolladores podría haberlo dificultado el análisis para un navegador. Cuanta más complejidad se agregue a una string, mayor será la probabilidad de que ocurran errores del analizador o diferencias involuntarias en el comportamiento de un navegador a otro.

Una ventana de codificación de imágenes automatizada.

Sin embargo, la misma característica que puede hacer que estos sujetos se sientan tan intimidantes también puede brindarte soluciones: una sintaxis que las máquinas leen con facilidad es una sintaxis que ellas escriben con más facilidad. Como usuario de la Web, lo más probable es que te encuentres con muchos ejemplos de compresión y codificación de imágenes automatizadas: cualquier imagen subida a la Web a través de plataformas de redes sociales, sistemas de administración de contenido (CMS) e incluso clientes de correo electrónico casi siempre pasarán por un sistema que cambia el tamaño, vuelve a codificarlos y los comprime.

Del mismo modo, el lenguaje de marcado de imágenes responsivas se presta fácilmente a la automatización, ya sea a través de complementos, bibliotecas externas, herramientas independientes del proceso de compilación o el uso responsable de secuencias de comandos del cliente.

Esas son las dos preocupaciones principales en torno a la automatización del rendimiento de la imagen: administrar la creación de imágenes (sus codificaciones, compresión y las fuentes alternativas que usarás para propagar un atributo de srcset) y generar nuestro lenguaje de marcado para el usuario. En este módulo, aprenderás sobre algunos enfoques comunes para administrar imágenes como parte de un flujo de trabajo moderno, ya sea como una fase automatizada de tu proceso de desarrollo, el framework o el sistema de administración de contenido que potencian tu sitio, o la abstracción casi abstracta de una red de distribución de contenidos dedicada.

Automatiza la compresión y la codificación

Es poco probable que te encuentres en una posición en la que puedas tomarte el tiempo necesario para determinar manualmente la codificación y el nivel de compresión ideales para cada imagen individual que se usará en un proyecto, o no lo desees. Por lo más importante que es mantener los tamaños de transferencia de imágenes lo más pequeños posible , ajustar la configuración de compresión y volver a guardar fuentes alternativas para cada recurso de imagen destinado a un sitio web de producción generaría un gran cuello de botella en tu trabajo diario.

Como aprendiste cuando leíste sobre los distintos formatos de imagen y tipos de compresión, la codificación más eficiente para una imagen siempre estará determinada por su contenido y, como aprendiste en Imágenes responsivas, los tamaños alternativos que necesitarás para las fuentes de imágenes se determinarán según la posición que ocupen esas imágenes en el diseño de la página. En un flujo de trabajo moderno, debes abordar estas decisiones de manera integral y no individual, es decir, determinar un conjunto de valores predeterminados razonables para las imágenes, a fin de que se adapten mejor a los contextos en los que deben usarse.

Cuando eliges codificaciones para un directorio de imágenes fotográficas, AVIF es el claro ganador en calidad y tamaño de transferencia, pero tiene compatibilidad limitada, WebP proporciona un resguardo moderno y optimizado, y JPEG es el valor predeterminado más confiable. Los tamaños alternativos que debemos producir para las imágenes destinadas a ocupar una barra lateral en el diseño de una página variarán mucho de las imágenes destinadas a ocupar todo el viewport del navegador en nuestros puntos de interrupción más altos. La configuración de compresión requerirá tener en cuenta los artefactos de desenfoque y compresión en varios archivos resultantes, lo que deja menos espacio para extraer cada byte posible de cada imagen a cambio de un flujo de trabajo más flexible y confiable. En resumen, seguirás el mismo proceso de toma de decisiones que llegaste a comprender en este curso, escrito a gran escala.

En cuanto al procesamiento en sí, hay una gran cantidad de bibliotecas de procesamiento de imágenes de código abierto que proporcionan métodos para convertir, modificar y editar imágenes en lotes, lo que compiten por la velocidad, la eficiencia y la confiabilidad. Estas bibliotecas de procesamiento te permitirán aplicar configuraciones de codificación y compresión a directorios completos de imágenes a la vez, sin la necesidad de abrir un software de edición de imágenes, y de una manera que preserve las fuentes de imágenes originales en caso de que esa configuración deba ajustarse sobre la marcha. Están diseñados para ejecutarse en una variedad de contextos, desde tu entorno de desarrollo local hasta el servidor web en sí; por ejemplo, el ImageMin enfocado en la compresión para Node.js se puede extender para adaptarse a aplicaciones específicas a través de un array de complementos, mientras que ImageMagick multiplataforma y el Sharp basado en Node.js vienen con una cantidad escalonada de funciones desde el primer momento.

Estas bibliotecas de procesamiento de imágenes permiten a los desarrolladores compilar herramientas dedicadas a optimizar imágenes sin problemas como parte de tus procesos de desarrollo estándar, lo que garantiza que tu proyecto siempre haga referencia a fuentes de imágenes listas para la producción con la menor sobrecarga posible.

Herramientas y flujos de trabajo de desarrollo locales

Los ejecutores de tareas y los agrupadores como Grunt, Gulp o Webpack se pueden usar para optimizar los recursos de imagen junto con otras tareas comunes relacionadas con el rendimiento, como la reducción de CSS y JavaScript. A modo de ejemplo, veamos un caso de uso relativamente simple: un directorio de tu proyecto contiene una docena de imágenes fotográficas diseñadas para usarse en un sitio web público.

En primer lugar, deberás asegurarte de que estas imágenes se codifiquen de forma eficaz y coherente. Como aprendiste en los módulos anteriores, WebP es una configuración predeterminada eficiente para imágenes fotográficas en términos de calidad y tamaño de archivo. WebP es bien compatible, pero no universalmente compatible, por lo que también querrás incluir un resguardo en forma de JPEG progresivo. Luego, a fin de usar el atributo srcset para publicar estos elementos de manera eficiente, deberás producir varios tamaños alternativos para cada codificación.

Si bien esta sería una tarea repetitiva que demandaría mucho tiempo si se hiciera con un software de edición de imágenes, los ejecutores de tareas como Gulp están diseñados para automatizar exactamente este tipo de repetición. El complemento gulp-responsive, que usa Nitidez, es una opción entre muchas que siguen un patrón similar: recopilar todos los archivos en un directorio del código fuente, volver a codificarlos y comprimirlos según la misma abreviatura estandarizada de “calidad” que aprendiste en Formatos de imagen y compresión. Los archivos resultantes se envían a una ruta de acceso que definas, listos para que se haga referencia a ellos en los atributos src de tus elementos img para el usuario, a la vez que dejan intactos los archivos originales.

const { src, dest } = require('gulp');
const respimg = require('gulp-responsive');

exports.webp = function() {
  return src('./src-img/*')
    .pipe(respimg({
      '*': [{
        quality: 70,
        format: ['webp', 'jpeg'],
        progressive: true
      }]
  }))
  .pipe(dest('./img/'));
}

Con un proceso como este implementado, no se dañaría un entorno de producción si alguien del proyecto agregaba de forma involuntaria una fotografía codificada como un archivo PNG con color verdadero masivo al directorio que contiene las fuentes de las imágenes originales. Independientemente de la codificación de la imagen original, esta tarea producirá un resguardo WebP eficaz y confiable en formato JPEG progresivo y con un nivel de compresión que se puede ajustar fácilmente sobre la marcha. Por supuesto, este proceso también garantiza que tus archivos de imagen originales se conservarán en el entorno de desarrollo del proyecto, lo que significa que esta configuración se puede ajustar en cualquier momento y solo se reemplaza el resultado automatizado.

Para generar varios archivos, debes pasar varios objetos de configuración, todos iguales, excepto que agregues una clave width y un valor en píxeles:

const { src, dest } = require('gulp');
const respimg = require('gulp-responsive');

exports.default = function() {
  return src('./src-img/*')
    .pipe(respimg({
    '*': [{
            width: 1000,
            format: ['jpeg', 'webp'],
            progressive: true,
            rename: { suffix: '-1000' }
            },
            {
            width: 800,
            format: ['jpeg', 'webp'],
            progressive: true,
            rename: { suffix: '-800' }
            },
            {
            width: 400,
            format: ['jpeg', 'webp'],
            progressive: true,
            rename: { suffix: '-400' },
        }]
        })
    )
    .pipe(dest('./img/'));
}

En el caso del ejemplo anterior, la imagen original (monarch.png) supera los 3.3 MB. El archivo más grande que genera esta tarea (monarch-1000.jpeg) es de aproximadamente 150 KB. La versión más pequeña, monarch-400.web, pesa solo 32 KB.

[10:30:54] Starting 'default'...
[10:30:54] gulp-responsive: monarch.png -> monarch-400.jpeg
[10:30:54] gulp-responsive: monarch.png -> monarch-800.jpeg
[10:30:54] gulp-responsive: monarch.png -> monarch-1000.jpeg
[10:30:54] gulp-responsive: monarch.png -> monarch-400.webp
[10:30:54] gulp-responsive: monarch.png -> monarch-800.webp
[10:30:54] gulp-responsive: monarch.png -> monarch-1000.webp
[10:30:54] gulp-responsive: Created 6 images (matched 1 of 1 image)
[10:30:54] Finished 'default' after 374 ms

Por supuesto, deberás examinar en detalle los resultados en busca de artefactos de compresión visibles, o posiblemente aumentar la compresión para obtener ahorros adicionales. Como esta tarea no es destructiva, la configuración se puede cambiar con facilidad.

En resumen, a cambio de los pocos kilobytes que podrías obtener con una cuidadosa microoptimización manual, obtendrás un proceso que no solo es eficiente, sino resistente, es una herramienta que aplica sin problemas tus conocimientos sobre recursos de imagen de alto rendimiento a todo un proyecto, sin ninguna intervención manual.

Lenguaje de marcado de imágenes responsivas en la práctica

Por lo general, completar atributos srcset será un proceso manual sencillo, ya que el atributo solo captura información sobre la configuración que ya realizaste cuando generaste tus fuentes. En las tareas anteriores, establecimos los nombres de los archivos y la información sobre el ancho que seguirá nuestro atributo:

srcset="filename-1000.jpg 1000w, filename-800.jpg 800w, filename-400.jpg 400w"

Recuerda que el contenido del atributo srcset es descriptivo, no prescriptivo. La sobrecarga de un atributo srcset no es perjudicial, siempre que la relación de aspecto de cada fuente sea coherente. Un atributo srcset puede contener el URI y el ancho de cada corte alternativo que genere el servidor sin generar solicitudes innecesarias. Además, cuantas más fuentes candidatas proporcionemos para una imagen renderizada, más eficientemente podrá el navegador adaptar las solicitudes.

Como aprendiste en Imágenes responsivas, te recomendamos usar el elemento <picture> para procesar sin problemas el patrón de resguardo de WebP o JPEG. En este caso, usarás el atributo type junto con srcset.

<picture>
  <source type="image/webp" srcset="filename-1000.webp 1000w, filename-800.webp 800w, filename-400.webp 400w">
  <img srcset="filename-1000.jpg 1000w, filename-800.jpg 800w, filename-400.jpg 400w" sizes="…" alt="…">
</picture>

Como aprendiste, los navegadores compatibles con WebP reconocerán el contenido del atributo type y seleccionarán el atributo srcset del elemento <source> como la lista de imágenes candidatas. Los navegadores que no reconozcan image/webp como un tipo de medio válido ignorarán este <source> y, en su lugar, usarán el atributo srcset del elemento <img> interno.

Un aspecto más respecto de la compatibilidad con el navegador es que los navegadores que no admiten lenguaje de marcado de imágenes responsivas necesitarán de todos modos un resguardo, o podríamos correr el riesgo de que una imagen no funcione en contextos de navegación particularmente antiguos. Debido a que <picture>, <source> y srcset se ignoran en estos navegadores, es mejor especificar una fuente predeterminada en el atributo src del <img> interno.

Debido a que escalar una imagen hacia abajo es visualmente fluido y la codificación JPEG es compatible universalmente, el JPEG más grande es una opción razonable.

<picture>
  <source type="image/webp" srcset="filename-1000.webp 1000w, filename-800.webp 800w, filename-400.webp 400w">
  <img src="filename-1000.jpg" srcset="filename-1000.jpg 1000w, filename-800.jpg 800w, filename-400.jpg 400w" sizes="…" alt="…">
</picture>

sizes puede ser un poco más difícil de manejar. Como aprendiste, sizes es necesariamente contextual: no puedes propagar el atributo sin conocer la cantidad de espacio que la imagen debe ocupar en el diseño renderizado. Para que las solicitudes sean lo más eficientes posibles, nuestro lenguaje de marcado debe incluir un atributo sizes exacto cuando el usuario final realice esas solicitudes, mucho antes de que se hayan solicitado los estilos que rigen el diseño de la página. Omitir sizes por completo no es solo un incumplimiento de la especificación HTML, sino que genera un comportamiento predeterminado equivalente a sizes="100vw" (se informa al navegador que esta imagen solo está restringida por el viewport, lo que da como resultado la selección de las fuentes candidatas más grandes posibles).

Como es el caso de cualquier tarea de desarrollo web particularmente engorrosa, se crearon varias herramientas para abstraer el proceso de escritura a mano de atributos sizes. respImageLint es un fragmento de código absolutamente esencial para examinar la exactitud de tus atributos sizes y proporcionar sugerencias de mejora. Se ejecuta como un marcador, una herramienta que ejecutas en tu navegador, mientras apunta a la página renderizada por completo que contiene los elementos de tu imagen. En un contexto en el que el navegador comprenda por completo el diseño de la página, también tendrá un reconocimiento casi perfecto en píxeles del espacio que debe ocupar una imagen en ese diseño en cada tamaño de viewport posible.

Informe de imágenes responsivas que muestra una discrepancia de tamaño/ancho.

Una herramienta para analizar con lint los atributos de sizes es ciertamente útil, pero tiene aún más valor como herramienta para generarlos de forma integral. Como sabes, las sintaxis srcset y sizes están diseñadas para optimizar las solicitudes de recursos de imagen de manera visualmente fluida. Aunque no es algo que se deba usar en producción, un valor de marcador de posición sizes predeterminado de 100vw es perfectamente razonable cuando se trabaja en el diseño de una página en tu entorno de desarrollo local. Una vez que hayas implementado los estilos de diseño, ejecutar respImageLint te proporcionará atributos sizes personalizados que puedes copiar y pegar en el lenguaje de marcado en un nivel de detalle mucho mayor que uno escrito a mano:

Informe de imágenes responsivas con dimensiones sugeridas.

Aunque las solicitudes de imágenes iniciadas por el lenguaje de marcado renderizado por el servidor ocurren demasiado rápido para que JavaScript genere un atributo sizes del cliente, el mismo razonamiento no se aplica si esas solicitudes se inician en el cliente. El proyecto Lazysizes, por ejemplo, te permite aplazar por completo las solicitudes de imagen hasta que se haya establecido el diseño, lo que permite que JavaScript genere nuestros valores sizes, una gran conveniencia para ti y una garantía de las solicitudes más eficientes posibles para tus usuarios. Sin embargo, ten en cuenta que este enfoque implica sacrificar la confiabilidad del lenguaje de marcado renderizado por el servidor y las optimizaciones de velocidad integradas en los navegadores, además de iniciar estas solicitudes solo después de que se haya procesado la página tendrá un impacto negativo enorme en tu puntuación de LCP.

Por supuesto, si ya dependes de un framework de renderización del cliente, como React o Vue, ya estarás incurriendo en una deuda. En esos casos, usar Lazysizes significa que tus atributos sizes se pueden abstraer casi por completo. Mejor aún: a medida que sizes="auto" en imágenes de carga diferida adquiere implementaciones nativas y consensos, Lazysizes se convertirá en un polyfill para el comportamiento del navegador estandarizado recientemente.