Bildoptimierung
Auf dieser Seite
Leg ein Bild in voller Größe in src/ oder public/ ab, richte ein <img> darauf, und sitelo skaliert es, wandelt es in ein modernes Format um und schreibt das Tag mit einem srcset neu. Nichts zu importieren, keine Komponente zu lernen.
Aktivieren
export default {
images: true,
}Die Kodierung übernimmt sharp, eine optionale Peer-Dependency — installiere sie zusammen mit sitelo, wenn du Bilder aktivierst:
npm install -D sharpWebsites, die auf Bildoptimierung verzichten, brauchen sie nie zu installieren.
Schreib ein ganz normales <img>
export default () => `
<html lang="de">
<body>
<img src="/images/hero.png" alt="Sonnenaufgang über dem Hafen">
</body>
</html>
`import { html, body, img } from 'javascript-to-html'
export default () =>
html({ lang: 'de' },
body(
img({ src: '/images/hero.png', alt: 'Sonnenaufgang über dem Hafen' }),
),
)export default function Home() {
return (
<html lang="de">
<body>
<img src="/images/hero.png" alt="Sonnenaufgang über dem Hafen" />
</body>
</html>
)
}Aus einer Quelle mit 3000×2000 wird auf der anderen Seite das hier:
<img src="/assets/img/hero.a1b2c3d4-3000.webp"
alt="Sonnenaufgang über dem Hafen"
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">Ein Bildschirm bis 400px Breite bekommt die 400er-Datei, bis 800 die 800er, bis 1200 die 1200er und alles Breitere die vollen 3000 — der Browser nimmt die erste Stufe, die den Viewport abdeckt (auf einem 2×-Display verdoppelt).
Was du bekommst
- Skalieren, das nie hochrechnet — jede konfigurierte Breite unterhalb der Quelle, dann die Breite der Quelle selbst obendrauf. Eine 750px-Quelle mit
widths: [400, 800, 1200]liefert 400 und 750 und hört dort auf. - Moderne Formate — ein Format ergibt ein schlichtes
<img srcset>; zwei oder mehr verpacken es in ein<picture>mit einem Rückfall im Ursprungsformat. - Kein Layout-Sprung —
widthundheightwerden aus dem echten Bild ergänzt, dazuloading="lazy"unddecoding="async". - Dasselbe Markup in der Entwicklung —
sitelo(dev) schreibt Seiten um und liefert Varianten auf Anfrage, du siehst also in der Vorschau, was du ausliefern wirst. - Ein gemeinsamer Cache — Varianten werden per Inhalts-Hash in
node_modules/.sitelo/imagesabgelegt, sodass Entwicklung und erneute Builds dasselbe Bild nie zweimal kodieren.
Das Umschreiben läuft über das gebaute HTML und erfasst damit Bilder aus src/ und public/ gleichermaßen.
Sobald ein Tag umgeschrieben ist, zeigt nichts mehr auf das Original, also wird es standardmäßig aus dem Build entfernt. Nur wirklich unreferenzierte Dateien gehen: jede HTML-, CSS-, JS-, XML- und JSON-Datei im Build wird durchsucht, ein Original aus einem <a href>, einem og:image, einem RSS-Enclosure, einem CSS-url() oder einem data-no-optimize-Tag bleibt also. Zur Laufzeit in einem Skript zusammengesetzte oder von einer Server-Island gerenderte URLs sieht es nicht — setze prune: false, wenn du solche hast.
Optionen
export default {
images: {
widths: [400, 800, 1200],
formats: ['avif', 'webp'],
quality: { avif: 55, webp: 78, jpeg: 82 },
exclude: ['**/og/**'],
},
}widths— Standard[400, 800, 1200]; die Breite der Quelle selbst bildet immer die oberste Stufeformats— Standard['webp'];avif,webp,jpeg,pngquality— Standard{ avif: 55, webp: 78, jpeg: 82 }; Encoder-Qualität je Format (png nutzt Kompression statt Qualität)sizes— dassizes-Attribut; einsizesam Tag gewinnt immerdimensions— Standardtrue; ergänztwidth/heightlazy— Standardtrue; ergänztloadingunddecodingexclude— Glob(s) oder RegExp(s) von Bild-URLs, die unangetastet bleibenassetsDir— Standard'assets/img'cacheDir— Standard'node_modules/.sitelo/images'remote— Standardfalse; optimierthttps://-Bilder zur Build-Zeitprune— Standardfalse; löscht Originale, die nichts im Build mehr referenziertdev— Standardtrue; setzefalse, um in der Entwicklung die unveränderten Originale auszuliefernconcurrency— parallele Kodierungen, Standard CPUs − 1 (max. 8); 1, wenn remote aktiv ist
Zwei Formate ergeben <picture>
AVIF ist kleiner, aber jünger als WebP — beide anzugeben lässt den Browser wählen und behält einen Rückfall für ältere Browser.
<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>Eine Größe, beim Namen genannt
Karussell-Thumbnails, Avatare, ein Raster aus Karten — manche Bilder werden immer nur in einer Größe gezeigt, und eine ganze Leiter ist für sie verschwendet. Die Dateinamen der Varianten tragen einen Content-Hash, du kannst sie also nicht von Hand schreiben; nenne stattdessen die Breite in der URL, und das Tag kommt mit genau dieser einen Datei heraus:
<img src="/images/hero.png?w=400" alt="Sonnenaufgang über dem Hafen">
<img src="/images/hero.png?w=400&format=jpeg" alt="Sonnenaufgang über dem Hafen">
<img src="/images/hero.png?w=200&h=200" alt="Sonnenaufgang über dem Hafen">
<img src="/images/hero.png?w=200&h=200&fit=contain" alt="Sonnenaufgang über dem Hafen">
<img src="/images/hero.png?w=200&h=200&background=fff" alt="Sonnenaufgang über dem Hafen">
<img src="/images/hero.png?w=200&h=200&position=top" alt="Sonnenaufgang über dem Hafen"><img src="/assets/img/hero.9f8e7d6c-400.webp"
alt="Sonnenaufgang über dem Hafen"
width="400" height="267"
loading="lazy" decoding="async">w— die Breite in Pixeln. Jede Breite funktioniert, nicht nur die auswidths; eine kleinere Quelle behält ihre eigene Größe, statt hochskaliert zu werden.h— die Höhe in Pixeln. Allein folgt die Breite dem Seitenverhältnis; zusammen mitwwird das Bild auf diese Box skaliert und zugeschnitten —?w=200&h=200ist ein quadratisches Thumbnail.fit— wie einew×h-Box erfüllt wird:cover(Standard) füllt sie und schneidet zu;containskaliert das ganze Bild in sie hinein, ohne Zuschnitt und ohne Ränder —width/heightsagen, was herauskam.background— füllt eincontain-Ergebnis bis zur exakten Box auf und impliziertfit=contain. Hex ohne#(fff,1a1a1a,ffffff80), ein CSS-Farbname odertransparent— das ein Format mit Alpha braucht; bei JPEG wird es schwarz.position— welchen Teil eincover-Zuschnitt behält. Standardmäßig die Mitte; eine Kante oder Ecke (top,left,right-bottom, …) oder sharps inhaltsbasierte Strategienentropy/attention.format—avif,webp,jpegoderpng. Allein behält es die volle Leiter, in diesem einen Format.
Sind mehrere formats konfiguriert, liefert eine feste Breite weiterhin ein <picture> mit einer Datei pro <source>. Die Namen sind die von vite-imagetools. Entfernte URLs werden nie geparst — ihr Query-String gehört dem Ursprung — und ist images aus, ignorieren statische Hosts den Query und liefern das Original.
Das Bild den Zuschnitt wählen lassen
Eine Kante oder Ecke ist vorhersehbar, aber in einem Karussell gemischter Fotos sitzt das Motiv selten zweimal an derselben Stelle. entropy und attention lassen sharp jedes Bild ansehen und das Zuschnittfenster dorthin schieben, wo es etwas Behaltenswertes findet:
<img src="/images/hero.png?w=300&h=300&position=entropy" alt="Sonnenaufgang über dem Hafen">
<img src="/images/team.jpg?w=300&h=300&position=attention" alt="Das Team">entropybehält die Region mit den meisten Details — Kanten, Textur, Farbvariation. Flacher Himmel, leere Wände und ruhige Hintergründe fallen zuerst weg. Gut für Landschaften, Produkte, alles, wo „viel los“ „interessant“ heißt.attentiongewichtet den Zuschnitt zu gesättigten Farben, hochfrequenter Helligkeit und Hauttönen — eine Heuristik dafür, wohin ein Mensch schauen würde. Gut für Fotos von Menschen: es findet meist Gesichter und Motive.
Beides sind einfache Heuristiken — kein Modell, kein Training —, also eher ein sinnvoller Standard für viele Bilder als eine Garantie für eines. Bei einem Hero-Bild, das dir wichtig ist, nenne die Kante. Die Wahl hängt nur vom Bild ab, ist also deterministisch und wird wie jede andere Variante gecacht.
Ausnahmen
Tags, die bereits ein srcset haben, in einem <picture> stecken oder auf ein SVG, ein animiertes GIF oder eine entfernte URL zeigen, bleiben unangetastet. Für alles andere sag es ausdrücklich:
<img src="/images/exact.png" alt="Pixel-Art" data-no-optimize>Bilder für Social Cards und Favicons stehen in <meta> und <link>, die hiervon nie umgeschrieben werden — sie behalten ihre feste URL.
Entfernte Bilder
Aus einem CMS importierte Inhalte zeigen oft auf den Server von jemand anderem. Aktiviere remote, und diese Bilder werden heruntergeladen, optimiert und von deiner eigenen Domain ausgeliefert:
export default {
images: {
remote: true,
},
}Ein fehlgeschlagener Abruf lässt das Tag exakt so, wie es war, samt Warnung — eine wackelige Quelle bringt deinen Build nie zu Fall. prune entfernt anschließend lokale Originale, die nichts mehr referenziert.