Wie ich meinen LCP um 70 % gesenkt habe
Lernen Sie fortgeschrittene Methoden zur Verbesserung der Core Web Vitals kennen

Verbesserung der LCP-Metriken mit Web Workers und zweistufigem Laden von Bildern
Meistens wird ein großes Bildelement im sichtbaren Viewport zum Largest Contentful Paint Element. Selbst nach Anwendung aller Lighthouse Best Practices wie Größenänderung von Bildern, Bildkomprimierung, WebP-Konvertierung und Preloading des LCP-Elements besteht Ihr Largest Contentful Paint die Core Web Vitals möglicherweise immer noch nicht.
Die einzige Möglichkeit, dies zu beheben, besteht darin, fortgeschrittenere Taktiken wie zweistufiges Laden und Threading Ihrer Seite mit Web Workers anzuwenden, um Ressourcen auf dem main thread freizugeben.

Zuletzt geprüft von Arjen Karel im März 2026
Ein wenig Hintergrund
Ich bin ein Pagespeed-Experte und meine Website ist mein Aushängeschild. Auf meiner Homepage behaupte ich stolz, dass meine Seite die schnellste der Welt ist. Deshalb muss meine Seite so schnell wie möglich laden, damit ich jeden Tropfen Pagespeed aus meiner Seite herausquetschen kann.
Die Techniken, die ich Ihnen heute zeige, sind für eine durchschnittliche (WordPress-)Seite ohne die Unterstützung eines engagierten und talentierten Entwicklerteams möglicherweise nicht praktikabel. Wenn Sie diese Technik auf Ihrer eigenen Seite nicht duplizieren können, ermutige ich Sie dennoch, den Artikel zu lesen und zu lernen, wie ich über Pagespeed denke und was meine Überlegungen sind.
Das Problem: große Bilder im sichtbaren Viewport
Ein großes Bild im sichtbaren Viewport wird oft zum Largest Contentful Paint Element. Es kommt häufig vor, dass dieses LCP-Bild die Core Web Vitals nicht besteht. Ich sehe solche Ergebnisse täglich.

Es gibt eine Reihe von Möglichkeiten, um sicherzustellen, dass dieses Element schnell auf dem Bildschirm erscheint:
- Preloaden des LCP-Elements. Das Preloading des LCP-Bildes stellt sicher, dass dieses Bild dem Browser so früh wie möglich zur Verfügung steht. Kombinieren Sie dies mit
fetchpriority="high", um dem Browser mitzuteilen, dass er dieses Bild gegenüber anderen Ressourcen priorisieren soll. - Verwenden Sie responsive Bilder. Stellen Sie sicher, dass Sie keine Bilder in Desktop-Größe an mobile Geräte ausliefern.
- Komprimieren Sie Ihre Bilder. Durch Bildkomprimierung lässt sich die Größe des Bildes drastisch reduzieren.
- Verwenden Sie Bildformate der nächsten Generation. Bildformate der nächsten Generation wie WebP übertreffen ältere Formate wie JPEG und PNG in fast allen Fällen.
- Minimieren Sie den critical rendering path. Eliminieren Sie alle render-blocking Ressourcen wie JavaScript und Stylesheets, die den LCP verzögern könnten.
Leider bestehen die LCP-Metriken trotz all dieser Optimierungen in einigen Fällen den Core Web Vitals Audit immer noch nicht. Warum? Die Größe des Bildes allein reicht aus, um die resource load duration Phase des LCP zu verzögern.
Die Lösung: zweistufiges Laden und Web Workers
Die Lösung, die ich implementiert habe (nachdem ich alle anderen Probleme auf meiner Seite optimiert hatte), ist das zweistufige Laden von Bildern.
Die Idee ist simpel: Beim ersten Render wird ein qualitativ minderwertiges Bild mit den exakt gleichen Abmessungen wie das endgültige, qualitativ hochwertige Bild angezeigt. Unmittelbar nach der Anzeige dieses Bildes beginnt der Prozess, der das qualitativ minderwertige Bild gegen ein qualitativ hochwertiges Bild austauscht.
Eine sehr einfache Implementierung könnte etwa so aussehen: Fügen Sie einem Bild zuerst einen load event listener hinzu. Wenn das Bild geladen wird, entfernt sich dieser event listener selbst und die src des Bildes wird gegen das endgültige, qualitativ hochwertige Bild ausgetauscht.
<img
width="100"
height="100"
alt="ein Alt-Text"
src="lq.webp"
onload="this.onload=null;this.src='hq.webp'"
>
Stufe 1: Low-Quality WebP 3-5kb

Stufe 2: High-Quality WebP 20-40kb

Das mag einfach genug erscheinen (und das ist es auch), aber der Austausch einer großen Anzahl von Bildern in einem frühen Stadium des Rendering-Prozesses wird zu viel Aktivität auf dem main thread verursachen und andere Core Web Vitals Metriken beeinträchtigen.
Deshalb habe ich mich entschieden, einen Teil der Arbeit an einen Web Worker auszulagern. Ein Web Worker läuft in einem neuen Thread und hat keinen direkten Zugriff auf die aktuelle Seite. Die Kommunikation zwischen dem Web Worker und der Seite erfolgt über ein Messaging-System. Der offensichtliche Vorteil ist, dass wir nicht den main thread der Seite selbst nutzen; wir geben dort Ressourcen frei. Der Nachteil ist, dass die Verwendung eines Web Worker etwas umständlich sein kann.
Der Prozess selbst ist nicht so schwierig. Sobald das DOMContentLoaded-Ereignis ausgelöst wurde, sammle ich alle Bilder auf der Seite. Wenn ein Bild geladen wurde, werde ich es sofort austauschen. Wenn es nicht geladen wurde (weil das Bild möglicherweise per Lazy Load geladen wird), füge ich einen Event Listener hinzu, der das Bild nach dem Lazy Load austauscht.
Ein wichtiger Vorbehalt: Der Browser behandelt jeden Bildaustausch als neuen LCP-Kandidaten. Wenn Ihr Austausch des qualitativ hochwertigen Bildes nach 2,5 Sekunden erfolgt, wird der LCP zum Zeitpunkt des Austauschs gemessen, nicht zum Zeitpunkt des Placeholders. Deshalb ist es wichtig, dass der Web Worker das Bild so schnell wie möglich abruft und austauscht.
Das Ergebnis: spektakulär.

Der Code für zweistufiges LCP-Laden über einen Web Worker
Hier ist der Code, den ich verwende, um meinen LCP durch zweistufiges Laden und einen Web Worker zu beschleunigen. Der Code auf der Hauptseite ruft einen Web Worker auf, der die Bilder abruft. Der Web Worker übergibt das Ergebnis als Blob an die Hauptseite. Nach Erhalt des Blobs wird das Bild ausgetauscht.
Worker.js
self.addEventListener('message', async event => {
const newimageURL = event.data.src.replace("/lq-","/resize-");
const response = await fetch(newimageURL)
const blob = await response.blob()
// Sende die Bilddaten an den UI-Thread!
self.postMessage({
uid: event.data.uid,
blob: blob,
})
})
Script.js
Die script.js wird als normales Skript auf der aktiven Webseite ausgeführt. Das Skript lädt zuerst den Worker. Dann durchläuft es alle Bilder auf einer Seite. Dies geschieht in einem frühen Stadium des Rendering-Prozesses. Ein Bild ist möglicherweise bereits geladen oder auch nicht. Wenn ein qualitativ minderwertiges Bild bereits geladen ist, wird der Austauschprozess sofort aufgerufen. Wenn es noch nicht geladen ist, fügt es dem Ladeereignis des Bildes einen Listener hinzu, der den Austauschprozess startet, sobald dieses Bild geladen ist.
Wenn ein Bild geladen wird, wird eine eindeutige ID für dieses Bild generiert. Dies ermöglicht es mir, das Bild auf der Seite leicht wiederzufinden (denken Sie daran, dass der Worker keinen Zugriff auf das DOM hat, so dass ich den DOM-Knoten des Bildes nicht senden kann). Die Bild-URL und die eindeutige ID werden dann an den Worker gesendet. Wenn der Worker das Bild abgerufen hat, wird es als Blob an das Skript zurückgesendet. Das Skript tauscht schließlich die alte Bild-URL gegen die vom Web Worker erstellte Blob-URL aus.
var myWorker = new Worker('/path-to/worker.js');
// Nachricht an den Worker senden
const sendMessage = (img) => {
// uid macht es einfacher, das Bild zu finden
var uid = create_UID();
// data-uid am Bildelement setzen
img.dataset.uid = uid;
// Nachricht an den Worker senden
myWorker.postMessage({ src: img.src, uid: uid });
};
// uid generieren
const create_UID = () => {
var dt = new Date().getTime();
var uuid = 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) {
var r = (new Date().getTime() + Math.random() * 16) % 16 | 0;
dt = Math.floor(dt / 16);
return (c == 'x' ? r : (r & 0x3 | 0x8)).toString(16);
});
return uid;
}
// Wenn wir ein Ergebnis vom Worker erhalten
myWorker.addEventListener('message', event => {
// Hole die Nachrichtendaten aus dem Ereignis
const imageData = event.data
// Hole das ursprüngliche Element für dieses Bild
const imageElement = document.querySelectorAll("img[data-uid='" + imageData.uid + "']");
// Wir können den `Blob` als Bildquelle verwenden! Wir müssen ihn nur zuerst
// in eine Objekt-URL umwandeln
const objectURL = URL.createObjectURL(imageData.blob)
// Sobald das Bild geladen ist, wollen wir ein wenig zusätzliches Cleanup durchführen
imageElement.onload = () => {
URL.revokeObjectURL(objectURL)
}
imageElement[0].setAttribute('src', objectURL)
})
// Alle Bilder holen
document.addEventListener("DOMContentLoaded", () => {
document.querySelectorAll('img[loading="lazy"]').forEach(
img => {
// Bild ist bereits sichtbar?
img.complete ?
// Sofort austauschen
sendMessage(img) :
// Beim Laden austauschen
img.addEventListener(
"load", i => { sendMessage(img) }, { once: true }
)
})
})
Um die LCP-Verbesserung in der Praxis zu verifizieren, verwenden Sie Real User Monitoring, um zu verfolgen, wie Ihre tatsächlichen Besucher die Seite erleben. Lab-Tools wie Lighthouse zeigen zwar die Verbesserung, aber was zählt, um die Core Web Vitals zu bestehen, sind Felddaten von echten Benutzern mit unterschiedlichen Verbindungen.
The RUM tool I built for my own clients.
CoreDash is what I use to audit enterprise platforms. Under 1KB tracking script, EU hosted, no consent banner. AI with MCP support built in. The same tool, available to everyone.
Create Free Account
