Automatiser la compression et l'encodage

Intégrez facilement la génération de sources d'images hautement performantes à votre processus de développement.

Toutes les syntaxes de ce cours, de l'encodage des données d'image au balisage dense des informations qui alimente les images responsives, sont des méthodes permettant aux machines de communiquer avec les machines. Vous avez découvert de nombreuses façons pour qu'un navigateur client communique ses besoins à un serveur et qu'un serveur réponde en nature. Le balisage d'image responsive (en particulier, srcset et sizes) permet de décrire une quantité choquante d'informations avec relativement peu de caractères. Pour le meilleur ou pour le pire, cette brièveté est inhérente à la conception: rendre ces syntaxes moins succinctes et plus faciles à analyser pour les développeurs aurait pu les rendre plus difficiles à analyser pour un navigateur. Plus une chaîne est complexe, plus il y a de risques d'erreurs d'analyse ou de différences de comportement involontaires d'un navigateur à l'autre.

Fenêtre d'encodage d'image automatisée.

Cependant, la même caractéristique qui peut rendre ces sujets intimidants peut également vous apporter des solutions: une syntaxe facilement lue par des machines est une syntaxe plus facilement écrite par elles. En tant qu'utilisateur du Web, vous avez très certainement déjà rencontré de nombreux exemples d'encodage et de compression d'images automatisés: toute image importée sur le Web via des plates-formes de réseaux sociaux, des systèmes de gestion de contenu (CMS) et même des clients de messagerie passe presque invariablement par un système qui les redimensionne, les re-encode et les compresse.

De même, que ce soit à travers des plug-ins, des bibliothèques externes, des outils de processus de compilation autonomes ou une utilisation responsable des scripts côté client, le balisage d'images responsives se prête facilement à l'automatisation.

Ce sont les deux principales préoccupations liées à l'automatisation des performances des images: la gestion de la création des images (leur encodage, leur compression et les autres sources que vous utiliserez pour renseigner un attribut srcset) et la génération du balisage visible par l'utilisateur. Dans ce module, vous allez découvrir quelques approches courantes de gestion des images dans le cadre d'un workflow moderne, qu'il s'agisse d'une phase automatisée du processus de développement, du framework ou du système de gestion de contenu qui alimente votre site, ou d'un réseau de diffusion de contenu dédié presque entièrement extrait.

Automatiser la compression et l'encodage

Il est peu probable que vous vous trouviez dans une position où vous puissiez prendre le temps de déterminer manuellement l'encodage et le niveau de compression optimaux pour chaque image destinée à être utilisée dans un projet, et vous ne le ferez pas non plus. Aussi important qu'il soit de réduire au maximum la taille de transfert d'images , affiner les paramètres de compression et réenregistrer des sources alternatives pour chaque élément image destiné à un site Web de production engendreraient un énorme goulot d'étranglement dans votre travail quotidien.

Comme vous l'avez appris en examinant les différents formats et types de compression d'image, l'encodage le plus efficace d'une image sera toujours dicté par son contenu. De même, comme vous l'avez appris dans Images responsives, les autres tailles dont vous aurez besoin pour vos sources d'images dépendront de la position que ces images occupent dans la mise en page. Dans un workflow moderne, vous allez aborder ces décisions de manière globale plutôt qu'individuellement, en déterminant un ensemble de valeurs par défaut raisonnables pour les images, afin de les adapter au mieux aux contextes dans lesquels elles sont destinées à être utilisées.

En ce qui concerne le choix des encodages pour un répertoire de photos, AVIF est clairement le meilleur en termes de qualité et de taille de transfert, mais sa compatibilité est limitée, WebP offre une solution de remplacement moderne et optimisée, et JPEG est la valeur par défaut la plus fiable. Les autres tailles que nous devons produire pour les images destinées à occuper une barre latérale dans une mise en page varient considérablement par rapport aux images destinées à occuper toute la fenêtre d'affichage du navigateur au niveau de nos points d'arrêt les plus élevés. Les paramètres de compression requièrent un œil sur le floutage et les artefacts de compression dans plusieurs fichiers obtenus, ce qui laisse moins de place à la taille de chaque octet possible de chaque image, en échange d'un workflow plus flexible et fiable. En résumé, vous suivrez le même processus de prise de décision que vous avez découvert dans ce cours, à grand angle.

Concernant le traitement en lui-même, il existe un grand nombre de bibliothèques Open Source de traitement d'images qui fournissent des méthodes de conversion, de modification et de retouche d'images par lot, en concurrence sur la vitesse, l'efficacité et la fiabilité. Ces bibliothèques de traitement vous permettent d'appliquer des paramètres d'encodage et de compression à des répertoires entiers d'images en une seule fois, sans avoir à ouvrir un logiciel d'édition d'images. Elles vous permettent également de préserver vos sources d'images d'origine si ces paramètres doivent être modifiés à la volée. Elles sont destinées à être exécutées dans différents contextes, de votre environnement de développement local au serveur Web lui-même. Par exemple, l'élément ImageMin pour Node.js, axé sur la compression, peut être étendu à des applications spécifiques grâce à un large éventail de plug-ins, tandis que les solutions multiplates-formes ImageMagick et Sharp, basées sur Node.js, proposent un nombre stupéfiant de fonctionnalités prêtes à l'emploi.

Ces bibliothèques de traitement d'images permettent aux développeurs de créer des outils dédiés à l'optimisation fluide des images dans le cadre de vos processus de développement standards. Votre projet peut ainsi toujours référencer des sources d'images prêtes pour la production avec le moins de frais généraux possible.

Outils et workflows de développement local

Les exécuteurs de tâches et les bundlers tels que Grunt, Gulp ou Webpack peuvent être utilisés pour optimiser les composants Image en même temps que d'autres tâches courantes liées aux performances, comme la réduction de la taille des ressources CSS et JavaScript. Pour illustrer cela, prenons un cas d'utilisation relativement simple: un répertoire de votre projet contient une douzaine d'images photo destinées à être utilisées sur un site Web public.

Tout d'abord, vous devez assurer un encodage cohérent et efficace pour ces images. Comme vous l'avez appris dans les modules précédents, WebP est une valeur par défaut efficace pour les images photo en termes de qualité et de taille de fichier. Le format WebP est bien compatible, mais pas universel. Vous devrez donc également inclure une création de remplacement sous la forme d'un fichier JPEG progressif. Ensuite, pour utiliser l'attribut srcset et diffuser efficacement ces éléments, vous devez produire plusieurs autres tailles pour chaque encodage.

Bien que cette tâche répétitive et fastidieuse s'applique à l'aide d'un logiciel de retouche d'images, les gestionnaires de tâches comme Gulp sont conçus pour automatiser exactement ce type de répétition. Le plug-in gulp-responsive, qui utilise Sharp, est une option parmi d'autres qui suit tous un modèle similaire : collecter tous les fichiers d'un répertoire source, les réencoder et les compresser selon le même raccourci de "qualité" standardisé que vous avez découvert dans Formats d'image et compression. Les fichiers obtenus sont ensuite générés dans un chemin d'accès que vous définissez. Il est prêt à être référencé dans les attributs src de vos éléments img visibles par l'utilisateur, tout en laissant vos fichiers d'origine intacts.

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/'));
}

Un tel processus en place ne causerait aucun dommage à un environnement de production si un membre du projet ajoutait par inadvertance une photo encodée au format PNG en couleur réelle au répertoire contenant vos sources d'image d'origine. Quel que soit l'encodage de l'image d'origine, cette tâche produira une création de remplacement JPEG progressive fiable et efficace, et à un niveau de compression facile à ajuster à la volée. Bien sûr, ce processus garantit également que vos fichiers image d'origine seront conservés dans l'environnement de développement du projet, ce qui signifie que ces paramètres peuvent être ajustés à tout moment, et que seule la sortie automatisée est écrasée.

Pour générer plusieurs fichiers, transmettez plusieurs objets de configuration, de même que vous ajoutez une clé width et une valeur en pixels:

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/'));
}

Dans le cas de l'exemple ci-dessus, la taille de l'image d'origine (monarch.png) excède 3,3 Mo. Le fichier généré par cette tâche (monarch-1000.jpeg plus volumineux) fait environ 150 Ko. La plus petite taille, monarch-400.web, ne fait que 32 Ko.

[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

Bien entendu, vous devrez examiner attentivement les résultats concernant les artefacts de compression visibles ou éventuellement augmenter la compression pour réaliser des économies supplémentaires. Cette tâche étant non destructive, ces paramètres peuvent être modifiés facilement.

En échange des quelques kilo-octets que vous pourriez éliminer grâce à une micro-optimisation manuelle minutieuse, vous obtenez un processus non seulement efficace, mais résilient. Cet outil applique parfaitement vos connaissances sur les composants Image hautes performances à l'ensemble d'un projet, sans aucune intervention manuelle.

Dans la pratique, le balisage d'images responsives

Le remplissage des attributs srcset est généralement un processus manuel simple, car ils ne capturent que les informations sur la configuration que vous avez déjà effectuée lors de la génération de vos sources. Dans les tâches ci-dessus, nous avons établi les noms de fichiers et les informations de largeur que notre attribut suivra:

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

N'oubliez pas que le contenu de l'attribut srcset est descriptif et non prescriptif. Surcharger un attribut srcset n'est pas préjudiciable, tant que le format de chaque source est cohérent. Un attribut srcset peut contenir l'URI et la largeur de chaque autre taille générée par le serveur sans entraîner de requêtes inutiles. Plus nous fournissons de sources candidates pour le rendu, plus le navigateur sera en mesure d'adapter les requêtes efficacement.

Comme vous l'avez appris dans la section Images responsives, vous devez utiliser l'élément <picture> pour gérer de manière transparente le modèle de remplacement WebP ou JPEG. Dans ce cas, vous devez utiliser l'attribut type conjointement avec 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>

Comme vous l'avez appris, les navigateurs compatibles avec WebP reconnaissent le contenu de l'attribut type et sélectionnent l'attribut srcset de cet élément <source> comme liste d'images candidates. Les navigateurs qui ne reconnaissent pas image/webp comme un type de média valide ignorent ce <source> et utilisent à la place l'attribut srcset de l'élément <img> interne.

Un autre point à prendre en compte concernant la compatibilité des navigateurs: les navigateurs qui ne sont pas compatibles avec le balisage d'images responsives devront tout de même recourir à une solution de remplacement. Dans le cas contraire, l'image risquerait d'être endommagée, en particulier dans les anciens contextes de navigation. Étant donné que <picture>, <source> et srcset sont tous ignorés dans ces navigateurs, nous devons spécifier une source par défaut dans l'attribut src du <img> interne.

Étant donné que la mise à l'échelle d'une image vers le bas est visuellement transparente et que l'encodage JPEG est universellement pris en charge, le plus grand format JPEG est un choix judicieux.

<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 peut être un peu plus difficile à gérer. Comme vous l'avez appris, sizes est nécessairement contextuel. Vous ne pouvez pas renseigner l'attribut sans connaître l'espace que l'image est censée occuper dans la mise en page affichée. Pour les requêtes les plus efficaces, un attribut sizes précis doit figurer dans notre balisage au moment où l'utilisateur final effectue les requêtes, bien avant que les styles qui régissent la mise en page aient été demandés. Omettre sizes constitue non seulement une violation de la spécification HTML, mais entraîne également un comportement par défaut équivalent à sizes="100vw", indiquant au navigateur que cette image n'est limitée que par la fenêtre d'affichage elle-même, ce qui permet de sélectionner les plus grandes sources candidates possibles.

Comme pour toute tâche de développement Web particulièrement fastidieuse, un certain nombre d'outils ont été créés pour éliminer le processus d'écriture manuscrite des attributs sizes. respImageLint est un extrait de code absolument essentiel pour vérifier la précision de vos attributs sizes et fournir des suggestions d'amélioration. Il s'exécute comme un favori intelligent. Il s'agit d'un outil que vous exécutez dans votre navigateur, tout en pointant vers la page entièrement affichée contenant vos éléments image. Dans un contexte où le navigateur connaît parfaitement la mise en page, il connaît également presque parfaitement l'espace qu'une image est censé occuper dans cette mise en page, pour toutes les tailles de fenêtre d'affichage possibles.

Rapport sur les images responsives montrant une incohérence entre la taille et la largeur.

Un outil d'analyse lint pour les attributs sizes est certes utile, mais il est encore plus utile pour les générer en gros. Comme vous le savez, la syntaxe srcset et sizes est destinée à optimiser les demandes d'assets image de manière visuellement fluide. Bien que cela ne doive jamais être utilisé en production, une valeur d'espace réservé sizes par défaut de 100vw est parfaitement raisonnable lorsque vous travaillez sur la mise en page d'une page dans votre environnement de développement local. Une fois les styles de mise en page en place, l'exécution de respImageLint fournit des attributs sizes personnalisés que vous pouvez copier et coller dans votre balisage, à un niveau de détail largement supérieur à celui écrit à la main:

Rapport sur les images responsives avec les dimensions suggérées

Bien que les requêtes d'images initiées par le balisage affiché par le serveur se produisent trop rapidement pour que JavaScript puisse générer un attribut sizes côté client, le même raisonment ne s'applique pas si ces requêtes sont initiées côté client. Le projet Lazysizes, par exemple, vous permet de différer entièrement les requêtes d'images jusqu'à ce que la mise en page soit établie. Cela permet à JavaScript de générer nos valeurs sizes pour nous, ce qui est très pratique pour vous et vous garantit que les requêtes sont les plus efficaces possible pour vos utilisateurs. Toutefois, n'oubliez pas que cette approche implique de sacrifier la fiabilité du balisage affiché par le serveur et les optimisations de vitesse intégrées aux navigateurs. Le fait de lancer ces requêtes uniquement après l'affichage de la page a un impact négatif considérable sur votre score LCP.

Bien sûr, si vous dépendez déjà d'un framework de rendu côté client tel que React ou Vue, c'est une dette que vous encourez déjà. Dans ce cas, l'utilisation de Lazysizes signifie que vos attributs sizes peuvent être presque complètement abstraits. Mieux encore: à mesure que sizes="auto" sur les images à chargement différé gagne en consensus et dans les implémentations natives, les Lazysizes deviendront en fait un polyfill pour ce nouveau comportement de navigateur standardisé.