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

Verbesserung der LCP-Metriken mit Web-Workern und 2-Stage Image Loading
Meistens wird eine große Bilddatei im sichtbaren Viewport zum Largest Contentful Paint Element. Selbst nach Anwendung aller Lighthouse Best-Practices wie Bildgrößenanpassung, Bildkomprimierung, WebP-Konvertierung und Preloading des LCP-Elements besteht Ihr Largest Contentful Paint möglicherweise immer noch nicht die Core Web Vitals.
Die einzige Möglichkeit, dies zu beheben, besteht in der Verwendung fortgeschrittenerer Taktiken wie 2-Stage Loading und Threading Ihrer Seite mit Web Workern, um Ressourcen im Main Thread freizugeben.
In diesem Artikel zeige ich, wie Sie den Largest Contentful Paint weiter verbessern können.

Ein wenig Hintergrund
Ich bin ein PageSpeed-Typ und meine Website ist mein Aushängeschild. Auf meiner Homepage behaupte ich stolz, dass meine Seite die schnellste Seite der Welt ist. Deshalb muss meine Seite so schnell wie möglich laden und jeden Tropfen PageSpeed aus meiner Seite herausholen.
Die Techniken, die ich Ihnen heute zeige, sind für Ihre durchschnittliche (WordPress) Seite ohne die Unterstützung eines engagierten und talentierten Dev-Teams möglicherweise nicht machbar. Wenn Sie diese Technik auf Ihrer eigenen Seite nicht duplizieren können, ermutige ich Sie dennoch, den Artikel zu lesen und zu erfahren, 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 oft vor, dass dieses LCP-Bild die Core Web Vitals nicht besteht. Ich sehe Ergebnisse wie diese täglich.

Es gibt eine Reihe von Möglichkeiten, sicherzustellen, dass dieses Element schnell auf dem Bildschirm erscheint:
- Preloaden Sie das LCP-Element. Das Preloaden des LCP-Bildes stellt sicher, dass dieses Bild dem Browser so früh wie möglich zur Verfügung steht.
- Verwenden Sie responsive Bilder. Stellen Sie sicher, dass Sie keine Bilder in Desktop-Größe an mobile Geräte ausliefern.
- Komprimieren Sie Ihre Bilder. Bildkomprimierung kann die Größe des Bildes drastisch reduzieren
- Verwenden Sie Next Gen Image Formate. Next Gen Image Formate 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 JavaScripts und Stylesheets, die den LCP verzögern könnten.
Leider bestehen die LCP-Metriken trotz all dieser Optimierungen in einigen Fällen immer noch nicht das Core Web Vitals Audit. Warum? Die Größe des Bildes allein reicht aus, um den LCP zu verzögern.
Die Lösung: 2-Stage Loading und Web Worker
Die Lösung, die ich implementiert habe (nachdem ich alle anderen Probleme auf meiner Seite optimiert hatte), ist 2-Stage Image Loading.
Die Idee ist einfach: Zeigen Sie beim ersten Rendern ein Bild mit niedriger Qualität mit genau denselben Abmessungen wie das endgültige Bild mit hoher Qualität an. Unmittelbar nachdem dieses Bild angezeigt wurde, starten Sie den Prozess, der das Bild mit niedriger Qualität gegen ein Bild mit hoher Qualität austauscht.
Eine sehr einfache Implementierung könnte ungefähr so aussehen: Fügen Sie zuerst einen Load Event Listener zu einem Bild hinzu. Wenn das Bild geladen wird, löst sich derselbe Event Listener von selbst und das src des Bildes wird gegen das endgültige Bild mit hoher Qualität ausgetauscht.
<img
width="100"
height="100"
alt="ein Alt-Text"
src="lq.webp"
onload="this.onload=null;this.src='hq.webp'"
> Stufe 1: niedrige Qualität webp 3-5kb 
Stufe 2: hohe Qualität webp 20-40kb 
Dies mag einfach genug erscheinen (und ist es auch), aber das Austauschen einer großen Anzahl von Bildern früh im Rendering-Prozess führt zu zu viel Aktivität im Main Thread und beeinträchtigt andere Core Web Vitals Metriken.
Deshalb habe ich mich entschieden, einen Teil der Arbeit auf einen Web Worker zu verlagern. Ein Web Worker läuft in einem neuen Thread und hat keinen wirklichen Zugriff auf die aktuelle Seite. Die Kommunikation zwischen dem Web Worker und der Seite erfolgt über ein Nachrichtensystem. Der offensichtliche Vorteil ist, dass wir den Main Thread der Seite selbst nicht verwenden, sondern dort Ressourcen freigeben. Der Nachteil ist, dass die Verwendung eines Web Workers etwas umständlich sein kann.
Der Prozess selbst ist nicht so schwierig. Sobald das DomContentLoaded-Event ausgelöst wurde, sammle ich alle Bilder auf der Seite. Wenn ein Bild geladen wurde, tausche ich es sofort aus. Wenn es nicht geladen wurde (weil das Bild lazy load sein könnte), füge ich einen Event Listener hinzu, der das Bild nach dem Lazy Load austauscht.
Das Ergebnis: spektakulär

Der Code für 2-Stage LCP Loading über einen Web Worker
Hier ist der Code, den ich verwende, um meinen LCP durch 2-Stage Loading und einen Web Worker zu beschleunigen. Der Code auf der Hauptseite ruft einen Webworker auf, der die Bilder abruft. Der Webworker ü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()
// Send the image data to the UI thread!
self.postMessage({
uid: event.data.uid,
blob: blob,
})
}) Script.js
Das 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 früh im Rendering-Prozess. Ein Bild könnte bereits geladen sein und könnte nicht. Wenn ein Bild mit niedriger Qualität bereits geladen ist, wird der Austauschprozess sofort aufgerufen. Wenn es noch nicht geladen ist, fügt es einen Listener an das Bild-Lade-Event an, der den Austauschprozess startet, sobald dieses Bild geladen ist..
Wenn ein Bild geladen ist, wird eine eindeutige ID für dieses Bild generiert. Dies ermöglicht es mir, das Bild auf der Seite leicht wiederzufinden (denken Sie daran, der Worker hat keinen Zugriff auf das DOM, daher kann ich den Bild-DOM-Knoten nicht senden).
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 Blob-URL aus, die vom Web Worker erstellt wurde.
var myWorker = new Worker('/path-to/worker.js');
// send a message to worker
const sendMessage = (img) => {
// uid makes it easier to find the image
var uid = create_UID();
// set data-uid on image element
img.dataset.uid = uid;
// send message to worker
myWorker.postMessage({ src: img.src, uid: uid });
};
// generate the uid
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;
}
// when we get a result from the worker
myWorker.addEventListener('message', event => {
// Grab the message data from the event
const imageData = event.data
// Get the original element for this image
const imageElement = document.querySelectorAll("img[data-uid='" + imageData.uid + "']");
// We can use the `Blob` as an image source! We just need to convert it
// to an object URL first
const objectURL = URL.createObjectURL(imageData.blob)
// Once the image is loaded, we'll want to do some extra cleanup
imageElement.onload = () => {
URL.revokeObjectURL(objectURL)
}
imageElement[0].setAttribute('src', objectURL)
})
// get all images
document.addEventListener("DOMContentLoaded", () => {
document.querySelectorAll('img[loading="lazy"]').forEach(
img => {
// image is already visible?
img.complete ?
// swap immediately
sendMessage(img) :
// swap on load
img.addEventListener(
"load", i => { sendMessage(img) }, { once: true }
)
})
}) Core Web Vitals Score mit gepreloadetem LCP-Bild
Secure your Q3 Metrics.
Do not let technical debt derail your Core Web Vitals. I provide the strategy, the code, and the verification to pass Google's assessment.
- Strategic Planning
- Code Implementation
- Verification & Testing

