Eine Bildkomponente enthält Best Practices für die Leistung und bietet eine sofort einsatzbereite Lösung zum Optimieren von Bildern.
Bilder sind eine häufige Quelle für Leistungsengpässe bei Webanwendungen und ein wichtiger Schwerpunktbereich für die Optimierung. Nicht optimierte Bilder tragen zur Aufblähung von Seiten bei und machen im 90. Perzentil über 70% der gesamten Webseitengröße in Byte aus.th Es gibt viele Möglichkeiten, Bilder zu optimieren. Daher ist eine intelligente „Bildkomponente“ mit standardmäßig integrierten Leistungslösungen erforderlich.
Das Aurora-Team hat mit Next.js zusammengearbeitet, um eine solche Komponente zu entwickeln. Ziel war es, eine optimierte Bildvorlage zu erstellen, die Webentwickler weiter anpassen können. Die Komponente dient als gutes Modell und setzt einen Standard für die Entwicklung von Bildkomponenten in anderen Frameworks, Content-Management-Systemen (CMS) und Tech-Stacks. Wir haben an einer ähnlichen Komponente für Nuxt.js mitgearbeitet und arbeiten mit Angular an der Bildoptimierung in zukünftigen Versionen. In diesem Beitrag wird beschrieben, wie wir die Next.js-Bildkomponente entwickelt haben und was wir dabei gelernt haben.

Probleme und Möglichkeiten bei der Bildoptimierung
Bilder wirken sich nicht nur auf die Leistung, sondern auch auf das Geschäft aus. Die Anzahl der Bilder auf einer Seite war der zweitwichtigste Faktor für Conversions von Nutzern, die Websites besuchen. Sitzungen, in denen Nutzer Conversions ausgeführt haben, enthielten 38% weniger Bilder als Sitzungen ohne Conversions. Lighthouse listet im Rahmen der Best Practices-Prüfung mehrere Optimierungsmöglichkeiten auf, um Bilder zu optimieren und die Web-Vitals zu verbessern. Hier sind einige der häufigsten Bereiche, in denen Bilder sich auf Core Web Vitals und die Nutzerfreundlichkeit auswirken können.
Bilder ohne Größenangabe wirken sich negativ auf den CLS aus
Wenn Bilder ohne Angabe der Größe bereitgestellt werden, kann dies zu Layoutinstabilität führen und zu einem hohen Cumulative Layout Shift (CLS) beitragen. Wenn Sie die Attribute width und height für <img>-Elemente festlegen, können Sie Layoutverschiebungen verhindern. Beispiel:
<img src="flower.jpg" width="360" height="240">
Breite und Höhe sollten so festgelegt werden, dass das Seitenverhältnis des gerenderten Bildes dem natürlichen Seitenverhältnis entspricht. Ein erheblicher Unterschied im Seitenverhältnis kann dazu führen, dass das Bild verzerrt aussieht. Mit einer relativ neuen Eigenschaft, mit der Sie das Seitenverhältnis in CSS angeben können, lassen sich Bilder responsiv skalieren und gleichzeitig CLS vermeiden.
Große Bilder können sich negativ auf den LCP auswirken
Je größer die Dateigröße eines Bildes ist, desto länger dauert der Download. Ein großes Bild kann das „Hero“-Bild für die Seite oder das wichtigste Element im Darstellungsbereich sein, das den Largest Contentful Paint (LCP) auslöst. Ein Bild, das Teil der wichtigen Inhalte ist und lange zum Herunterladen benötigt, verzögert den LCP.
In vielen Fällen können Entwickler die Bildgrößen durch eine bessere Komprimierung und die Verwendung reaktionsfähiger Bilder reduzieren. Mit den Attributen srcset und sizes des <img>-Elements können Bilddateien mit unterschiedlichen Größen bereitgestellt werden. Der Browser kann dann je nach Bildschirmgröße und ‑auflösung das richtige Bild auswählen.
Schlechte Bildkomprimierung kann sich negativ auf den LCP auswirken
Moderne Bildformate wie AVIF oder WebP bieten eine bessere Komprimierung als die häufig verwendeten JPEG- und PNG-Formate. Durch eine bessere Komprimierung kann die Dateigröße bei gleicher Bildqualität in einigen Fällen um 25% bis 50% reduziert werden. Dadurch werden Downloads beschleunigt und der Datenverbrauch gesenkt. Die App sollte moderne Bildformate für Browser bereitstellen, die diese Formate unterstützen.
Das Laden unnötiger Bilder beeinträchtigt den LCP
Bilder, die sich unter dem Falz oder nicht im sichtbaren Bereich befinden, werden dem Nutzer beim Laden der Seite nicht angezeigt. Sie können verzögert werden, damit sie nicht zum LCP beitragen und ihn verzögern. Mit Lazy Loading können solche Bilder erst dann geladen werden, wenn der Nutzer zu ihnen scrollt.
Herausforderungen bei der Optimierung
Teams können die Leistungskosten aufgrund der oben aufgeführten Probleme bewerten und Best-Practice-Lösungen implementieren, um sie zu beheben. In der Praxis ist das jedoch oft nicht der Fall und ineffiziente Bilder verlangsamen das Web weiterhin. Mögliche Gründe sind z. B. folgende:
- Prioritäten: Webentwickler konzentrieren sich in der Regel auf Code, JavaScript und Datenoptimierung. Daher sind sie sich möglicherweise nicht bewusst, dass es Probleme mit Bildern gibt oder wie sie optimiert werden können. Bilder, die von Designern erstellt oder von Nutzern hochgeladen wurden, haben möglicherweise keine hohe Priorität.
- Out-of-the-box-Lösung: Auch wenn Entwickler sich der Feinheiten der Bildoptimierung bewusst sind, kann das Fehlen einer All-in-One-Out-of-the-box-Lösung für ihr Framework oder ihren Tech-Stack ein Hindernis sein.
- Dynamische Bilder: Neben statischen Bildern, die Teil der Anwendung sind, werden dynamische Bilder von Nutzern hochgeladen oder aus externen Datenbanken oder CMS bezogen. Es kann schwierig sein, die Größe solcher Bilder zu definieren, wenn die Quelle des Bildes dynamisch ist.
- Überladung mit Markup: Lösungen zum Einbinden der Bildgröße oder von
srcsetfür verschiedene Größen erfordern zusätzliches Markup für jedes Bild, was mühsam sein kann. Das Attributsrcsetwurde 2014 eingeführt, wird aber heute nur von 26,5%der Websites verwendet. Wenn Entwicklersrcsetverwenden, müssen sie Bilder in verschiedenen Größen erstellen. Tools wie just-gimme-an-img können hilfreich sein, müssen aber für jedes Bild manuell verwendet werden. - Browserunterstützung: Moderne Bildformate wie AVIF und WebP erzeugen kleinere Bilddateien, müssen aber in Browsern, die sie nicht unterstützen, speziell behandelt werden. Entwickler müssen Strategien wie Content Negotiation oder das
<picture>-Element verwenden, damit Bilder in allen Browsern angezeigt werden. - Komplikationen beim Lazy Loading: Es gibt mehrere Techniken und Bibliotheken, mit denen sich Lazy Loading für Bilder unterhalb des Falzes implementieren lässt. Die Auswahl der besten Option kann schwierig sein. Entwickler wissen möglicherweise auch nicht, in welcher Entfernung vom „Fold“ verzögert geladene Bilder am besten platziert werden. Unterschiedliche Viewport-Größen auf Geräten können die Sache noch komplizierter machen.
- Sich ständig verändernde Landschaft: Da Browser neue HTML- oder CSS-Funktionen zur Leistungssteigerung unterstützen, kann es für Entwickler schwierig sein, jede einzelne zu bewerten. In Chrome wird beispielsweise die Funktion Fetch Priority als Ursprungstest eingeführt. Damit lässt sich die Priorität bestimmter Bilder auf der Seite erhöhen. Insgesamt wäre es für Entwickler einfacher, wenn solche Verbesserungen auf Komponentenebene bewertet und implementiert würden.
Bildkomponente als Lösung
Die Möglichkeiten zur Optimierung von Bildern und die Herausforderungen bei der individuellen Implementierung für jede Anwendung haben uns auf die Idee einer Bildkomponente gebracht. Eine Bildkomponente kann Best Practices kapseln und erzwingen. Wenn Entwickler das <img>-Element durch eine Bildkomponente ersetzen, können sie die Probleme mit der Bildleistung besser in den Griff bekommen.
Im letzten Jahr haben wir mit dem Next.js-Framework zusammengearbeitet, um die Bildkomponente zu entwerfen und implementieren. Es kann wie folgt als direkter Ersatz für die vorhandenen <img>-Elemente in Next.js-Apps verwendet werden.
// Before with <img> element:
function Logo() {
return <img src="/logo.jpg" alt="logo" height="200" width="100" />
}
// After with image component:
import Image from 'next/image'
function Logo() {
return <Image src="/logo.jpg" alt="logo" height="200" width="100" />
}
Die Komponente versucht, bildbezogene Probleme durch eine Vielzahl von Funktionen und Prinzipien zu beheben. Außerdem enthält es Optionen, mit denen Entwickler es an verschiedene Bildanforderungen anpassen können.
Schutz vor Layoutverschiebungen
Wie bereits erwähnt, verursachen Bilder ohne Größenangaben Layoutverschiebungen und tragen zum CLS bei. Wenn Entwickler die Next.js-Bildkomponente verwenden, müssen sie mit den Attributen width und height eine Bildgröße angeben, um Layoutverschiebungen zu vermeiden. Wenn die Größe unbekannt ist, müssen Entwickler layout=fill angeben, um ein Bild ohne Größenangabe in einem Container mit Größenangabe bereitzustellen. Alternativ können Sie statische Bildimporte verwenden, um die Größe des tatsächlichen Bildes auf der Festplatte zur Build-Zeit abzurufen und in das Bild einzufügen.
// Image component with width and height specified
<Image src="/logo.jpg" alt="logo" height="200" width="100" />
// Image component with layout specified
<Image src="/hero.jpg" layout="fill" objectFit="cover" alt="hero" />
// Image component with image import
import Image from 'next/image'
import logo from './logo.png'
function Logo() {
return <Image src={logo} alt="logo" />
}
Da Entwickler die Image-Komponente nicht ohne Größenangabe verwenden können, wird durch das Design sichergestellt, dass sie sich die Zeit nehmen, die Bildgröße zu berücksichtigen und Layout Shifts zu verhindern.
Reaktionsfähigkeit fördern
Damit Bilder auf allen Geräten responsiv dargestellt werden, müssen Entwickler die Attribute srcset und sizes im Element <img> festlegen. Mit der Bildkomponente wollten wir diesen Aufwand reduzieren. Die Next.js-Bildkomponente ist so konzipiert, dass die Attributwerte nur einmal pro Anwendung festgelegt werden. Sie werden je nach Layoutmodus auf alle Instanzen der Bildkomponente angewendet. Wir haben eine dreiteilige Lösung entwickelt:
- Property
deviceSizes: Mit dieser Property können Sie Breakpoints einmalig basierend auf den Geräten konfigurieren, die für die Nutzerbasis der Anwendung üblich sind. Die Standardwerte für Breakpoints sind in der Konfigurationsdatei enthalten. - Attribut
imageSizes: Dies ist ebenfalls ein konfigurierbares Attribut, mit dem die Bildgrößen für die Breakpoints der Gerätegröße abgerufen werden. layout-Attribut in jedem Bild: Damit wird angegeben, wie die PropertiesdeviceSizesundimageSizesfür jedes Bild verwendet werden sollen. Die unterstützten Werte für den Layoutmodus sindfixed,fill,intrinsicundresponsive.
Wenn ein Bild mit den Layoutmodi responsive oder fill angefordert wird, ermittelt Next.js das zu bereitstellende Bild anhand der Größe des Geräts, das die Seite anfordert, und legt die srcset und sizes im Bild entsprechend fest.
Im folgenden Vergleich sehen Sie, wie Sie mit dem Layoutmodus die Größe des Bildes auf verschiedenen Bildschirmen steuern können. Wir haben ein Demobild aus der Next.js-Dokumentation verwendet, das auf einem Smartphone und einem Standardlaptop angezeigt wird.
| Laptopbildschirm | Smartphone-Display |
|---|---|
| Layout = „Intrinsic“: Die Größe wird auf kleineren Viewports so angepasst, dass sie in die Breite des Containers passt. Das Bild wird in einem größeren Darstellungsbereich nicht über seine intrinsische Größe hinaus skaliert. Containerbreite ist auf 100 % festgelegt | |
|
|
| Layout = „Fixed“ (Fest): Das Bild ist nicht responsiv. Breite und Höhe sind wie beim |
|
|
|
| Layout = „Responsiv“: Die Größe wird je nach Breite des Containers auf verschiedenen Viewports verkleinert oder vergrößert, wobei das Seitenverhältnis beibehalten wird. | |
|
|
Layout = „Fill“ (Ausfüllen): Breite und Höhe werden so angepasst, dass der übergeordnete Container ausgefüllt wird. In diesem Beispiel ist die Breite des übergeordneten Elements <div> auf 300 × 500 festgelegt.
|
|
|
|
Integriertes Lazy Loading bereitstellen
Die Image-Komponente bietet standardmäßig eine integrierte, leistungsstarke Lazy-Loading-Lösung. Bei der Verwendung des <img>-Elements gibt es einige Optionen für das Lazy Loading, die jedoch alle Nachteile haben, die die Verwendung erschweren. Entwickler können eine der folgenden Lazy-Loading-Methoden verwenden:
- Geben Sie das Attribut
loadingan: Dieses Attribut wird von allen modernen Browsern unterstützt. - Intersection Observer API verwenden: Die Entwicklung einer benutzerdefinierten Lazy-Loading-Lösung erfordert Aufwand sowie ein durchdachtes Design und eine durchdachte Implementierung. Entwickler haben dafür möglicherweise nicht immer Zeit.
- Importieren einer Drittanbieterbibliothek für das Lazy Loading von Bildern: Es kann zusätzlicher Aufwand erforderlich sein, um eine geeignete Drittanbieterbibliothek für das Lazy Loading zu bewerten und zu integrieren.
In der Next.js-Bildkomponente ist „loading“ standardmäßig auf "lazy" festgelegt. Lazy Loading wird mit Intersection Observer implementiert, das in den meisten modernen Browsern verfügbar ist. Entwickler müssen nichts weiter tun, um die Funktion zu aktivieren. Sie können sie aber bei Bedarf deaktivieren.
Wichtige Bilder vorab laden
Häufig sind LCP-Elemente Bilder. Große Bilder können den LCP verzögern. Es empfiehlt sich, wichtige Bilder vorzuladen, damit der Browser sie schneller findet. Wenn Sie ein <img>-Element verwenden, kann im HTML-Head ein Preload-Hinweis wie unten gezeigt eingefügt werden.
<link rel="preload" as="image" href="important.png">
Eine gut gestaltete Bildkomponente sollte eine Möglichkeit bieten, die Ladereihenfolge von Bildern unabhängig vom verwendeten Framework anzupassen. Bei der Next.js-Bildkomponente können Entwickler mit dem Attribut priority der Bildkomponente angeben, dass ein Bild ein guter Kandidat für das Vorabladen ist.
<Image src="/hero.jpg" alt="hero" height="400" width="200" priority />
Durch das Hinzufügen eines priority-Attributs wird das Markup vereinfacht und die Verwendung ist komfortabler. Entwickler von Bildkomponenten können auch Optionen zum Anwenden von Heuristiken in Betracht ziehen, um das Vorladen von Bildern, die bestimmte Kriterien erfüllen und sich über dem Falz auf der Seite befinden, zu automatisieren.
Hochleistungsfähiges Bild-Hosting fördern
Bild-CDNs werden empfohlen, um die Bildoptimierung zu automatisieren. Sie unterstützen auch moderne Bildformate wie WebP und AVIF. Die Next.js-Bildkomponente verwendet standardmäßig ein Bild-CDN mit einer Loader-Architektur. Das folgende Beispiel zeigt, dass der Loader die Konfiguration des CDN in der Next.js-Konfigurationsdatei ermöglicht.
module.exports = {
images: {
loader: 'imgix',
path: 'https://ImgApp/imgix.net',
},
}
Mit dieser Konfiguration können Entwickler relative URLs in der Bildquelle verwenden. Das Framework verkettet die relative URL mit dem CDN-Pfad, um die absolute URL zu generieren. Beliebte Bild-CDNs wie Imgix, Cloudinary und Akamai werden unterstützt. Die Architektur unterstützt die Verwendung eines benutzerdefinierten Cloud-Anbieters durch die Implementierung einer benutzerdefinierten loader-Funktion für die App.
Selbst gehostete Bilder unterstützen
Es kann vorkommen, dass Websites keine Bild-CDNs verwenden können. In solchen Fällen muss eine Bildkomponente selbst gehostete Bilder unterstützen. Die Next.js-Bildkomponente verwendet einen Bildoptimierer als integrierten Bildserver, der eine CDN-ähnliche API bietet. Der Optimierer verwendet Sharp für die Transformation von Produktionsbildern, sofern es auf dem Server installiert ist. Diese Bibliothek ist eine gute Wahl für alle, die eine eigene Pipeline zur Bildoptimierung erstellen möchten.
Progressives Laden unterstützen
Progressives Laden ist eine Technik, mit der das Interesse der Nutzer aufrechterhalten wird. Dabei wird ein Platzhalterbild angezeigt, das in der Regel eine deutlich geringere Qualität hat, während das eigentliche Bild geladen wird. Dadurch wird die wahrgenommene Leistung verbessert und die Nutzerfreundlichkeit erhöht. Sie kann in Kombination mit Lazy Loading für Bilder unterhalb des Falzes oder für Bilder oberhalb des Falzes verwendet werden.
Die Next.js-Bildkomponente unterstützt das progressive Laden von Bildern über die placeholder-Eigenschaft. Dies kann als LQIP (Low-Quality Image Placeholder) verwendet werden, um ein Bild mit niedriger Qualität oder ein unscharfes Bild anzuzeigen, während das eigentliche Bild geladen wird.
Auswirkungen
Dank all dieser Optimierungen haben wir mit der Next.js-Bildkomponente in der Produktion gute Ergebnisse erzielt. Wir arbeiten auch mit anderen Technologien an ähnlichen Bildkomponenten.
Als Leboncoin das alte JavaScript-Frontend auf Next.js migriert hat, wurde auch die Bildpipeline auf die Next.js-Bildkomponente umgestellt. Auf einer Seite, die von <img> zu next/image migriert wurde, sank der LCP von 2,4 Sekunden auf 1,7 Sekunden. Die Gesamtzahl der für die Seite heruntergeladenen Bild-Bytes sank von 663 KB auf 326 KB (mit etwa 100 KB für lazy-loaded Bilder).
Gewonnene Erkenntnisse
Jeder, der eine Next.js-App erstellt, kann von der Verwendung der Next.js-Bildkomponente zur Optimierung profitieren. Wenn Sie jedoch ähnliche Leistungsabstraktionen für ein anderes Framework oder CMS erstellen möchten, können Ihnen die folgenden Erkenntnisse helfen, die wir im Laufe der Zeit gewonnen haben.
Sicherheitsventile können mehr schaden als nützen
In einer frühen Version der Next.js-Bildkomponente haben wir ein unsized-Attribut bereitgestellt, mit dem Entwickler die Größenanforderung umgehen und Bilder mit nicht angegebenen Abmessungen verwenden konnten. Wir dachten, dass dies in Fällen erforderlich wäre, in denen die Höhe oder Breite des Bildes im Voraus nicht bekannt ist. Wir haben jedoch festgestellt, dass Nutzer in GitHub-Problemen das unsized-Attribut als Universallösung für Probleme mit der Größenanforderung empfehlen, auch wenn sie das Problem auf eine Weise lösen könnten, die den CLS nicht verschlechtert. Wir haben das Attribut unsized später eingestellt und entfernt.
Nützliche von unnötiger Reibung trennen
Die Anforderung, die Größe eines Bildes anzupassen, ist ein Beispiel für „nützliche Reibung“. Dadurch wird die Verwendung der Komponente eingeschränkt, aber im Gegenzug bietet sie erhebliche Leistungsvorteile. Nutzer akzeptieren die Einschränkung eher, wenn sie sich der potenziellen Leistungssteigerungen bewusst sind. Daher ist es sinnvoll, diesen Kompromiss in der Dokumentation und anderen veröffentlichten Materialien zur Komponente zu erläutern.
Sie können jedoch Problemumgehungen für solche Reibungspunkte finden, ohne die Leistung zu beeinträchtigen. Während der Entwicklung der Next.js-Bildkomponente haben wir beispielsweise Beschwerden erhalten, dass es lästig sei, die Größen für lokal gespeicherte Bilder nachzuschlagen. Wir haben statische Bildimporte hinzugefügt, die diesen Prozess optimieren, indem sie mithilfe eines Babel-Plug-ins automatisch Abmessungen für lokale Bilder zur Build-Zeit abrufen.
Ausgewogenheit zwischen Komfortfunktionen und Leistungsoptimierungen finden
Wenn Ihre Bildkomponente nichts anderes tut, als ihren Nutzern „nützliche Reibung“ aufzuerlegen, werden Entwickler sie wahrscheinlich nicht verwenden wollen. Wir haben festgestellt, dass Leistungsmerkmale wie die Bildgröße und die automatische Generierung von srcset-Werten am wichtigsten waren. Entwicklerfreundliche Funktionen wie automatisches Lazy Loading und integrierte unscharfe Platzhalter haben ebenfalls das Interesse an der Next.js-Bildkomponente gesteigert.
Roadmap für Funktionen zur Förderung der Akzeptanz festlegen
Eine Lösung zu entwickeln, die in allen Situationen perfekt funktioniert, ist sehr schwierig. Es kann verlockend sein, etwas zu entwerfen, das für 75% der Nutzer gut funktioniert, und den anderen 25% zu sagen, dass diese Komponente in diesen Fällen nicht für sie geeignet ist.
In der Praxis stellt sich heraus, dass diese Strategie nicht mit Ihren Zielen als Komponentendesigner übereinstimmt. Sie möchten, dass Entwickler Ihre Komponente verwenden, um von den Leistungssteigerungen zu profitieren. Das ist schwierig, wenn es eine Gruppe von Nutzern gibt, die nicht migrieren können und sich ausgeschlossen fühlen. Sie werden wahrscheinlich ihre Enttäuschung äußern, was zu negativen Wahrnehmungen führt, die sich auf die Akzeptanz auswirken.
Es empfiehlt sich, einen langfristigen Fahrplan für Ihre Komponente zu erstellen, der alle angemessenen Anwendungsfälle abdeckt. Es ist auch hilfreich, in der Dokumentation explizit anzugeben, was nicht unterstützt wird und warum, um Erwartungen hinsichtlich der Probleme zu wecken, die die Komponente lösen soll.
Fazit
Die Verwendung und Optimierung von Bildern ist kompliziert. Entwickler müssen ein Gleichgewicht zwischen Leistung und Qualität von Bildern finden und gleichzeitig für eine gute Nutzererfahrung sorgen. Die Bildoptimierung ist daher ein kostspieliges Unterfangen mit großer Wirkung.
Damit nicht jede App das Rad jedes Mal neu erfinden muss, haben wir eine Best-Practice-Vorlage erstellt, die Entwickler, Frameworks und andere Tech-Stacks als Referenz für ihre eigenen Implementierungen verwenden können. Diese Erfahrung wird sich als wertvoll erweisen, wenn wir andere Frameworks und ihre Bildkomponenten unterstützen.
Die Next.js-Bildkomponente hat die Leistungsergebnisse in Next.js-Anwendungen erfolgreich verbessert und damit die Nutzerfreundlichkeit erhöht. Wir sind der Meinung, dass es ein großartiges Modell ist, das im gesamten Ökosystem gut funktionieren würde, und würden uns freuen, von Entwicklern zu hören, die dieses Modell in ihren Projekten einsetzen möchten.