Comment j'ai réduit mon LCP de 70 %
Apprenez des méthodes avancées pour améliorer les Core Web Vitals
Améliorer les métriques LCP avec les Web Workers et le chargement d'images en 2 étapes<\/h2>
La plupart du temps, un grand élément d'image dans le viewport visible deviendra l'élément Largest Contentful Paint. Même après avoir appliqué toutes les bonnes pratiques de Lighthouse comme le redimensionnement des images, la compression, la conversion au format WebP et le préchargement de l'élément LCP, votre [url=\/core-web-vitals\/largest-contentful-paint]Largest Contentful Paint (LCP)[\/url] pourrait toujours ne pas passer les Core Web Vitals.<\/p>
La seule façon de corriger cela est d'utiliser des tactiques plus avancées comme le chargement en 2 étapes et l'utilisation de Web Workers pour libérer des ressources sur le thread principal.<\/p> <\/div>

Dernière révision par [url=https:\/\/www.linkedin.com\/in\/arjenkarel\/]Arjen Karel[\/url] en mars 2026<\/p>
Un peu de contexte<\/h2>
Je suis un spécialiste de la vitesse de page et mon site web est ma vitrine. Sur ma page d'accueil, je proclame fièrement que mon site est le plus rapide au monde. C'est pourquoi j'ai besoin que ma page se charge aussi vite que possible et que j'extraie chaque goutte de performance de mon site.<\/p>
Les techniques que je vais vous montrer aujourd'hui pourraient ne pas être viables pour un site (WordPress) moyen sans le soutien d'une équipe de développement dédiée et talentueuse. Si vous ne pouvez pas reproduire cette technique sur votre propre site, je vous encourage tout de même à lire l'article pour apprendre comment je réfléchis à la vitesse de page et quelles sont mes considérations.<\/p>
Le problème : de grandes images dans le viewport visible<\/h2>
Une grande image dans le viewport visible deviendra souvent l'élément Largest Contentful Paint. Il arrive fréquemment que cette image LCP ne passe pas les Core Web Vitals. Je vois des résultats comme celui-ci quotidiennement.<\/p>
Il existe several façons de s'assurer que cet élément apparaisse rapidement à l'écran :<\/p> Malheureusement, malgré toutes ces optimisations, dans certains cas, les métriques LCP pourraient encore ne pas passer l'audit des Core Web Vitals. Pourquoi ? La taille de l'image seule suffit à retarder la phase de [url=\/core-web-vitals\/largest-contentful-paint\/resource-load-duration]durée de chargement de la ressource[\/url] du LCP.<\/p> La solution que j'ai mise en œuvre (après avoir optimisé tous les autres problèmes sur mon site) est le chargement d'images en 2 étapes. <\/p> L'idée est simple : au premier rendu, afficher une image de basse qualité ayant exactement les mêmes dimensions que l'image finale de haute qualité. Immédiatement après l'affichage de cette image, lancez le processus qui remplace l'image de basse qualité par une image de haute qualité.<\/p> Une implémentation très basique pourrait ressembler à ceci : tout d'abord, ajoutez un écouteur d'événement de chargement à une image. Lorsque l'image se charge, cet écouteur se détache et la source (src) de l'image est remplacée par l'image finale de haute qualité. <\/p>
fetchpriority="high"<\/code> pour dire au navigateur de prioriser cette image par rapport aux autres ressources.<\/li>
La solution : le chargement en 2 étapes et les Web Workers<\/h2>
<imgwidth="100"height="100"alt="un texte alternatif"src="lq.webp"onload="this.onload=null;this.src='hq.webp'"
><\/pre>Étape 1 : WebP basse qualité 3-5 ko<\/i>
<\/p>
Étape 2 : WebP haute qualité 20-40 ko<\/i>
<\/p>
<\/div>
Cela peut paraître assez simple (et ça l'est), mais remplacer un grand nombre d'images au début du processus de rendu provoquera trop d'activité sur le thread principal et affectera d'autres métriques Core Web Vitals.<\/p>C'est pourquoi j'ai choisi de décharger une partie du travail sur un Web Worker. Un Web Worker s'exécute dans un nouveau thread et n'a pas d'accès réel à la page actuelle. La communication entre le Web Worker et la page se fait via un système de messagerie. L'avantage évident est que nous n'utilisons pas le thread principal de la page lui-même ; nous y libérons des ressources. L'inconvénient est que l'utilisation d'un Web Worker peut être un peu laborieuse.<\/p>Le processus lui-même n'est pas si difficile. Une fois l'événement DOMContentLoaded déclenché, je collecte toutes les images de la page. Si une image est déjà chargée, je la remplace immédiatement. Si elle n'est pas encore chargée (parce qu'elle pourrait être en lazy loading), je lui attache un écouteur d'événement qui remplace l'image après son chargement.<\/p>Une mise en garde importante : le navigateur traite chaque remplacement d'image comme un nouveau candidat LCP. Si votre remplacement d'image haute qualité intervient après 2,5 secondes, le LCP sera mesuré au moment du remplacement, et non au moment de l'image de substitution. C'est pourquoi il est crucial que le Web Worker récupère et remplace l'image aussi vite que possible.<\/p>Le résultat : spectaculaire.<\/p><\/two-stage-img><\/p>
Le code pour le chargement LCP en 2 étapes via un Web Worker<\/h2>
Voici le code que j'utilise pour accélérer mon LCP grâce au chargement en 2 étapes et à un Web Worker. Le code sur la page principale appelle un Web Worker qui va récupérer les images. Le Web Worker transmet le résultat sous forme de blob à la page principale. À la réception du blob, l'image est remplacée.<\/p>Worker.js<\/h3>Le worker a un seul rôle. Il écoute les messages. Un message contient l'URL d'une image et un identifiant unique. Tout d'abord, il transforme l'URL de l'image vers la version haute qualité. Dans mon cas, en remplaçant /lq par /resize dans l'URL de l'image. Le worker va ensuite récupérer l'image haute qualité, créer un blob et renvoyer le blob de l'image accompagné de l'identifiant unique.<\/div>self.addEventListener('message', async event => {
const newimageURL = event.data.src.replace("/lq-","\/resize-");
const response = await fetch(newimageURL)
const blob = await response.blob()
\/\/ Envoyez les données de l'image au thread UI !
self.postMessage({
uid: event.data.uid,
blob: blob,
})
})<\/pre>
Script.js<\/h3>
Le fichier script.js s'exécutera comme un script normal sur la page web active. Le script charge d'abord le worker. Ensuite, il parcourt toutes les images d'une page. Cela se produit tôt dans le processus de rendu. Une image peut déjà être chargée ou non. Si une image de basse qualité est déjà chargée, il appelle immédiatement le processus de remplacement. Si elle n'est pas encore chargée, il attache un écouteur à l'événement de chargement de l'image qui lance le processus de remplacement dès que cette image est chargée.<\/p>Lorsqu'une image est chargée, un identifiant unique est généré pour celle-ci. Cela me permet de retrouver facilement l'image sur la page (n'oubliez pas que le worker n'a pas accès au DOM, donc je ne peux pas lui envoyer le nœud DOM de l'image). L'URL de l'image et l'identifiant unique sont ensuite envoyés au worker. Une fois que le worker a récupéré l'image, elle est renvoyée au script sous forme de blob. Le script finit par remplacer l'ancienne URL de l'image par l'URL du blob créée par le Web Worker.<\/p>var myWorker = new Worker('\/path-to\/worker.js');\/\/ envoyez un message au worker
const sendMessage = (img) => {\/\/ l'uid facilite la recherche de l'imagevar uid = create_UID();\/\/ définissez data-uid sur l'élément imageimg.dataset.uid = uid;\/\/ envoyez le message au workermyWorker.postMessage({ src: img.src, uid: uid });
};\/\/ générez l'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;
}\/\/ quand nous recevons un résultat du worker
myWorker.addEventListener('message', event => {\/\/ Récupérez les données du message depuis l'événementconst imageData = event.data\/\/ Récupérez l'élément d'origine pour cette imageconst imageElement = document.querySelectorAll("img[data-uid='" + imageData.uid + "']");\/\/ Nous pouvons utiliser le Blob comme source d'image ! Nous devons juste le convertir\/\/ en une URL d'objet d'abordconst objectURL = URL.createObjectURL(imageData.blob)\/\/ Une fois l'image chargée, nous voudrons effectuer un nettoyage supplémentaireimageElement.onload = () => {URL.revokeObjectURL(objectURL)}imageElement[0].setAttribute('src', objectURL)
})\/\/ récupérez toutes les images
document.addEventListener("DOMContentLoaded", () => {document.querySelectorAll('img[loading="lazy"]').forEach(img => {\/\/ l'image est déjà visible ?img.complete ?\/\/ remplacez immédiatementsendMessage(img) :\/\/ remplacez au chargementimg.addEventListener("load", i => { sendMessage(img) }, { once: true })})
})<\/pre>Pour vérifier l'amélioration du LCP sur le terrain, utilisez le [url=https:\/\/coredash.app]Real User Monitoring (RUM)[\/url] pour suivre l'expérience réelle de vos visiteurs sur la page. Les outils de laboratoire comme Lighthouse montreront l'amélioration, mais ce sont les données de terrain provenant d'utilisateurs réels sur des connexions variées qui comptent pour réussir les Core Web Vitals.<\/p>[include]cwv\/authorbio.html[\/include]<\/div>[include]sidebar.html[\/include]<\/div><\/div>
<\/section>
[include]blogfooter.html[\/include]
CoreWebVitalsInternational PageSpeed and Core Web Vitals ConsultantStationsplein 11
4611 BX Bergen op Zoom
Netherlands
KvK: 27307732
+31 164 313023
info@corewebvitals.io
linkedinLearn PageSpeed
Cache warmer gratuit pour site web
L'impact des CSS View Transitions sur les performances web
Defer vs Async en JavaScript et comment cela affecte les Core Web Vitals
« Use efficient cache lifetimes » dans Lighthouse : comment le corriger
La checklist ultime des Core Web Vitals (2026)
Empêchez Coralogix RUM de voler votre réseau en début de chargement
JPEG XL et Core Web Vitals : ce que vous devez savoir maintenant que Chrome le prend en charge
Arborescence des dépendances réseau dans Lighthouse : la lire et la corriger
Corriger « Layout shift culprits » dans Lighthouse et PageSpeed Free toolsCore Web Vitals report
Critical CSS Generator
WP Core Web Vitals plugin
Chrome Trace Event ManualAboutTerms and Conditions
Privacy
Français
Nederlands
English
Deutsch
Français
Español
Italiano
日本語
Português (Brasil)
한국어
Português
const pagecfg={"label":"pagespeed"};