Optimisation des images
Sur cette page
Déposez une image en pleine taille dans src/ ou public/, pointez une balise <img> dessus, et sitelo la redimensionne, la convertit dans un format moderne et réécrit la balise avec un srcset. Rien à importer, aucun composant à apprendre.
Activation
export default {
images: true,
}L’encodage est assuré par sharp, une dépendance peer optionnelle — installez-la aux côtés de sitelo quand vous activez les images :
npm install -D sharpLes sites qui n’optimisent pas les images n’ont jamais besoin de l’installer.
Écrivez un <img> ordinaire
export default () => `
<html lang="fr">
<body>
<img src="/images/hero.png" alt="Lever de soleil sur le port">
</body>
</html>
`import { html, body, img } from 'javascript-to-html'
export default () =>
html({ lang: 'fr' },
body(
img({ src: '/images/hero.png', alt: 'Lever de soleil sur le port' }),
),
)export default function Home() {
return (
<html lang="fr">
<body>
<img src="/images/hero.png" alt="Lever de soleil sur le port" />
</body>
</html>
)
}Une source de 3000×2000 ressort ainsi :
<img src="/assets/img/hero.a1b2c3d4-3000.webp"
alt="Lever de soleil sur le port"
sizes="100vw"
width="3000" height="2000"
loading="lazy" decoding="async"
srcset="/assets/img/hero.9f8e7d6c-400.webp 400w,
/assets/img/hero.5b4a3c2d-800.webp 800w,
/assets/img/hero.7c6d5e4f-1200.webp 1200w,
/assets/img/hero.a1b2c3d4-3000.webp 3000w">Un écran jusqu’à 400px de large reçoit le fichier de 400, jusqu’à 800 celui de 800, jusqu’à 1200 celui de 1200, et tout écran plus large le 3000 complet — le navigateur prend le premier échelon qui couvre la fenêtre (doublé sur un écran 2×).
Ce que vous obtenez
- Un redimensionnement qui n’agrandit jamais — chaque largeur configurée en dessous de celle de la source, puis la largeur de la source elle-même au sommet. Une source de 750px avec
widths: [400, 800, 1200]produit 400 et 750, et s’arrête là. - Des formats modernes — un seul format donne un
<img srcset>ordinaire ; deux ou plus l’enveloppent dans un<picture>avec un repli dans le format d’origine. - Aucun décalage de mise en page —
widthetheightsont renseignés depuis l’image réelle, avecloading="lazy"etdecoding="async". - Le même balisage en développement —
sitelo(dev) réécrit les pages et sert les variantes à la demande : vous prévisualisez ce que vous livrez. - Un cache partagé — les variantes sont indexées par hachage de contenu dans
node_modules/.sitelo/images, si bien que le développement et les rebuilds n’encodent jamais deux fois la même image.
La réécriture s’applique au HTML construit : elle couvre donc aussi bien les images de src/ que celles de public/.
Une fois la balise réécrite, plus rien ne pointe vers l’original : il est donc retiré du build par défaut. Seuls les fichiers vraiment sans référence partent : chaque HTML, CSS, JS, XML et JSON du build est parcouru, si bien qu’un original lié depuis un <a href>, un og:image, une enclosure RSS, un url() CSS ou une balise data-no-optimize reste. Les URL assemblées à l’exécution dans un script, ou rendues par un îlot serveur, sont celles qu’il ne peut pas voir — mettez prune: false si vous en avez.
Options
export default {
images: {
widths: [400, 800, 1200],
formats: ['avif', 'webp'],
quality: { avif: 55, webp: 78, jpeg: 82 },
exclude: ['**/og/**'],
},
}widths— par défaut[400, 800, 1200]; la largeur de la source elle-même coiffe toujours l’échelleformats— par défaut['webp'];avif,webp,jpeg,pngquality— par défaut{ avif: 55, webp: 78, jpeg: 82 }; qualité d’encodage par format (png utilise la compression, pas la qualité)sizes— l’attributsizes; unsizesprésent sur la balise l’emporte toujoursdimensions— par défauttrue; ajoutewidth/heightlazy— par défauttrue; ajouteloadingetdecodingexclude— glob(s) ou RegExp d’URL d’images à laisser intactesassetsDir— par défaut'assets/img'cacheDir— par défaut'node_modules/.sitelo/images'remote— par défautfalse; optimise les imageshttps://au buildprune— par défautfalse; supprime les originaux que plus rien dans le build ne référencedev— par défauttrue; mettezfalsepour servir les originaux intacts en développementconcurrency— encodages en parallèle, par défaut CPU − 1 (max 8) ; 1 quand remote est actif
Deux formats vous donnent <picture>
AVIF est plus léger mais plus récent que WebP : lister les deux laisse le navigateur choisir tout en gardant un repli pour les anciens.
<picture>
<source type="image/avif" srcset="/assets/img/hero.*-400.avif 400w, ..." sizes="...">
<source type="image/webp" srcset="/assets/img/hero.*-400.webp 400w, ..." sizes="...">
<img src="/assets/img/hero.*-3000.png" alt="..." srcset="..." width="3000" height="2000">
</picture>Une taille, par son nom
Vignettes d’un carrousel, avatars, grille de cartes : certaines images ne s’affichent jamais qu’à une seule taille, et toute une échelle est gaspillée pour elles. Les noms des variantes portent un hash du contenu, vous ne pouvez donc pas les écrire à la main ; indiquez plutôt la largeur dans l’URL, et la balise ressort avec ce seul fichier :
<img src="/images/hero.png?w=400" alt="Lever de soleil sur le port">
<img src="/images/hero.png?w=400&format=jpeg" alt="Lever de soleil sur le port">
<img src="/images/hero.png?w=200&h=200" alt="Lever de soleil sur le port">
<img src="/images/hero.png?w=200&h=200&fit=contain" alt="Lever de soleil sur le port">
<img src="/images/hero.png?w=200&h=200&background=fff" alt="Lever de soleil sur le port">
<img src="/images/hero.png?w=200&h=200&position=top" alt="Lever de soleil sur le port"><img src="/assets/img/hero.9f8e7d6c-400.webp"
alt="Lever de soleil sur le port"
width="400" height="267"
loading="lazy" decoding="async">w— la largeur en pixels. N’importe quelle largeur convient, pas seulement celles dewidths; une source plus petite garde sa propre taille au lieu d’être agrandie.h— la hauteur en pixels. Seule, la largeur suit le rapport d’aspect ; avecwen plus, l’image est mise à l’échelle pour remplir cette boîte puis rognée —?w=200&h=200donne une vignette carrée.fit— comment une boîtew×hest respectée :cover(par défaut) la remplit et rogne ;containréduit l’image entière pour qu’elle tienne dedans, sans rognage ni marges —width/heightdisent ce qui en est sorti.background— complète un résultatcontainjusqu’à la boîte exacte, et impliquefit=contain. Hex sans#(fff,1a1a1a,ffffff80), un nom de couleur CSS outransparent— qui demande un format avec alpha ; en jpeg, il ressort noir.position— quelle partie un rognagecoverconserve. Centré par défaut ; un bord ou un coin (top,left,right-bottom, …), ou les stratégies de sharp guidées par le contenu,entropy/attention.format—avif,webp,jpegoupng. Seul, il conserve l’échelle complète, dans ce seul format.
Avec plusieurs formats configurés, une largeur fixée donne toujours un <picture>, avec un fichier par <source>. Les noms sont ceux de vite-imagetools. Les URL distantes ne sont jamais analysées — leur query string appartient à l’origine — et, avec images désactivé, les hébergeurs statiques ignorent la query et servent l’original.
Laisser l’image choisir le rognage
Un bord ou un coin est prévisible, mais dans un carrousel de photos variées, le sujet est rarement deux fois au même endroit. entropy et attention demandent à sharp de regarder chaque image et de déplacer la fenêtre de rognage là où il trouve quelque chose qui vaut la peine d’être gardé :
<img src="/images/hero.png?w=300&h=300&position=entropy" alt="Lever de soleil sur le port">
<img src="/images/team.jpg?w=300&h=300&position=attention" alt="L’équipe">entropyconserve la région la plus détaillée — contours, texture, variations de couleur. Le ciel uni, les murs vides et les fonds plats partent en premier. Bon pour les paysages, les produits, tout ce où « chargé » veut dire « intéressant ».attentionoriente le rognage vers les couleurs saturées, la luminance à haute fréquence et les tons chair — une heuristique de l’endroit où une personne regarderait. Bon pour les photos de personnes : il trouve généralement les visages et les sujets.
Ce sont deux simples heuristiques — pas de modèle, pas d’entraînement — donc un bon défaut pour beaucoup d’images plutôt qu’une garantie pour une seule. Pour une image de une qui vous tient à cœur, nommez le bord. Le choix ne dépend que de l’image : il est déterministe et se met en cache comme toute autre variante.
Exclure des images
Les balises qui ont déjà un srcset, qui se trouvent dans un <picture>, ou qui pointent vers un SVG, un GIF animé ou une URL distante sont laissées intactes. Pour les autres, dites-le explicitement :
<img src="/images/exact.png" alt="Pixel art" data-no-optimize>Les images de cartes sociales et les favicons vivent dans des <meta> et des <link>, que cela ne réécrit jamais — elles conservent leur URL fixe.
Images distantes
Le contenu importé d’un CMS pointe souvent vers le serveur de quelqu’un d’autre. Activez remote et ces images sont téléchargées, optimisées et servies depuis votre propre domaine :
export default {
images: {
remote: true,
},
}Un téléchargement qui échoue laisse la balise exactement telle quelle, avec un avertissement — une origine instable ne fait jamais échouer votre build. Ensuite, prune supprime les originaux locaux que plus rien ne référence.