Optimisation des performances frontend : guide Core Web Vitals pour viser le score parfait

Lighthouse affiche seulement 60 points, et il faut atteindre 90 en une semaine. Ouvrez Chrome DevTools : LCP, FID, CLS — il faut comprendre ce que signifient ces métriques. Pour les développeurs frontend, l’optimisation des performances arrive souvent comme un travail supplémentaire, sans préavis.
J’ai beaucoup trébuché : lors de ma première optimisation, j’ai mis du lazy loading sur toutes les images — le score LCP a baissé. La grande image hero au-dessus de la ligne de flottaison ne doit surtout pas être lazy-loadée. Une autre fois, j’ai passé deux jours sur une stratégie de cache Service Worker pour gagner seulement 2 points. Cet article n’est pas un exposé théorique : c’est un guide pratique qui produit des résultats visibles en 2 semaines — quelles optimisations offrent le meilleur ROI, quels pièges éviter, et comment faire passer Lighthouse de 60 à 90+ par ordre de priorité.
Chapitre 1 : Comprendre les trois Core Web Vitals
Ne vous laissez pas impressionner par ces acronymes anglais — en résumé : la page charge-t-elle vite, réagit-elle aux clics, le layout reste-t-il stable ?
Qu’est-ce que les Core Web Vitals ?
Google a introduit en 2020 trois métriques clés d’expérience utilisateur. Elles influencent non seulement l’UX, mais aussi directement le classement SEO. Même avec un excellent contenu, une mauvaise performance fait chuter le ranking. En mars 2024, une mise à jour majeure : l’INP remplace le FID comme métrique centrale. Si vous optimisez encore le FID, vous êtes en retard.
LCP — Largest Contentful Paint
Le LCP mesure la vitesse de chargement du contenu principal — en général la grande image hero, un titre ou une vidéo au-dessus de la ligne de flottaison. Les seuils :
- Moins de 2,5 secondes : bon (vert)
- 2,5–4 secondes : à améliorer (jaune)
- Plus de 4 secondes : mauvais (rouge)
J’aime comparer le LCP au service du plat principal au restaurant. Si vous attendez dix minutes sans voir arriver le plat, vous serez mécontent, non ?
Chiffre concret : quand le temps de chargement passe de 3 à 5 secondes, le taux de rebond augmente de 38 %. Sur mobile, si le chargement dépasse 3 secondes, 53 % des utilisateurs partent. Un LCP bien optimisé peut augmenter la conversion de 7 à 15 %.
INP — Interaction to Next Paint
C’est la nouvelle métrique depuis mars 2024, qui remplace officiellement le FID. L’INP mesure la réactivité de la page aux interactions — clics, saisie clavier, toucher — sur tout le cycle de réponse. Les seuils :
- Moins de 200 ms : bon (vert)
- 200–500 ms : à améliorer (jaune)
- Plus de 500 ms : mauvais (rouge)
Pourquoi Google a-t-il remplacé le FID ? Le FID ne mesure que le « First Input Delay », tandis que l’INP couvre tout le processus d’interaction. Comme au restaurant : le FID vérifie si le serveur vous a entendu ; l’INP mesure le temps entre la commande et l’arrivée du plat.
Lors de mon premier audit, l’INP d’un projet était à 650 ms — un clic sur un bouton prenait plus d’une demi-seconde, pas étonnant que les utilisateurs trouvent la page saccadée. La cause : une intégration YouTube automatique ; après suppression, l’INP est descendu à 220 ms — la fluidité était nettement perceptible.
CLS — Cumulative Layout Shift
Le CLS mesure la stabilité visuelle de la page. Vous connaissez ce cas : vous allez cliquer sur un bouton, la page saute soudain — et vous touchez une pub à côté ? C’est le CLS. Les seuils :
- Moins de 0,1 : bon (vert)
- 0,1–0,25 : à améliorer (jaune)
- Plus de 0,25 : mauvais (rouge)
La première fois que j’ai vu un CLS à 0,5, je pensais que c’était « pas si grave » — en réalité, c’était déjà une zone rouge. Causes fréquentes : images sans dimensions, publicités insérées brusquement, flash de texte invisible (FOIT).
Chapitre 2 : Optimisation LCP — le meilleur ROI en performance
Pourquoi je place le LCP en premier ?
- Impact immédiat : les utilisateurs le ressentent tout de suite
- Plus grand levier : un LCP typique peut passer de 5 s à moins de 2 s
- Solutions matures : optimisation d’images, CDN — tout est éprouvé
- ROI maximal : peu d’effort, résultats rapides — le meilleur rapport qualité/prix
Processus complet d’optimisation Core Web Vitals
Solution complète LCP, INP et CLS — Lighthouse de 60 à 90+ en 2 semaines
Estimated time: PT2W
-
1
Step 1: Priorité P0 : optimisation des images (LCP)
Les images causent plus de 70 % des problèmes LCP — ROI le plus élevé : -
2
Step 2: • AVIF
41 % de compression en plus (vs JPEG) -
3
Step 3: • WebP
30 % de compression en plus (vs JPEG) -
4
Step 4: • Next.js
<Image priority /> -
5
Step 5: Cas réel
image hero de 500 Ko à 120 Ko (AVIF), LCP de 4,2 s à 2,1 s. -
6
Step 6: Priorité P0 : scripts tiers différés (INP)
Les scripts tiers sont le principal goulot d’étranglement INP : -
7
Step 7: Cas réel
suppression de l’embed YouTube auto, INP de 650 ms à 220 ms. -
8
Step 8: Priorité P0 : dimensions des images (CLS)
Le CLS est la métrique la plus facile à corriger : -
9
Step 9: Cas réel
dimensions sur toutes les images, CLS de 0,35 à 0,05. -
10
Step 10: Priorité P1 : code splitting et ressources
Code splitting et optimisation des ressources : -
11
Step 11: • React
React.lazy() -
12
Step 12: • Vue
defineAsyncComponent() -
13
Step 13: Cas réel
back-office admin de 1,2 Mo à 200 Ko au first view, INP de 450 ms à 180 ms. -
14
Step 14: Priorité P1 : polices et serveur
Polices et optimisation serveur : -
15
Step 15: Priorité P2 : optimisations avancées
Optimisations avancées (ROI moyen, difficulté élevée) :
1. Optimisation des images (ROI : ⭐⭐⭐⭐⭐)
C’est le levier le plus important — plus de 70 % des problèmes LCP viennent des images. Je commence toujours par là.
a) Formats d’image modernes
Ne sous-estimez pas le format : passer du JPEG à l’AVIF peut économiser 300 Ko sur une seule image.
<!-- Option 1 : balise <picture>, le navigateur choisit le meilleur format -->
<picture>
<source srcset="hero.avif" type="image/avif">
<source srcset="hero.webp" type="image/webp">
<img src="hero.jpg" alt="Hero image"> <!-- fallback pour navigateurs anciens -->
</picture>
// Option 2 : optimisation automatique Next.js (recommandé)
import Image from 'next/image'
<Image
src="/hero.jpg"
width={1200}
height={600}
priority // Important : images LCP avec priority — pas de lazy loading !
/>
Chiffres concrets :
- AVIF : 41 % de compression en plus vs JPEG
- WebP : 30 % de compression en plus vs JPEG
- Cas réel : page e-commerce — hero de 500 Ko à 120 Ko (AVIF), LCP de 4,2 s à 2,1 s — divisé par deux !
b) Lazy loading (sauf images LCP)
Piège classique : lazy-loader aussi l’image LCP — le score baisse alors.
<!-- ❌ Erreur : ne pas lazy-loader les images LCP ! -->
<img src="hero.jpg" loading="lazy">
<!-- ✅ Correct : eager au-dessus de la ligne de flottaison, lazy en dessous -->
<img src="hero.jpg" loading="eager"> <!-- image LCP -->
<img src="product1.jpg" loading="lazy"> <!-- images plus bas -->
<img src="product2.jpg" loading="lazy">
Règle : aucune image visible au premier écran ne doit être lazy-loadée. Le lazy loading sert aux images sous la ligne de flottaison.
c) Images responsives
Les utilisateurs mobile n’ont pas besoin des grandes images desktop — srcset économise jusqu’à 60 % de bande passante.
<img
src="hero-800w.jpg"
srcset="hero-400w.jpg 400w,
hero-800w.jpg 800w,
hero-1200w.jpg 1200w"
sizes="(max-width: 600px) 400px,
(max-width: 1000px) 800px,
1200px"
alt="Hero"
/>
Effet réel : sur mobile, charger une image 400w au lieu de 1200w améliore le LCP d’environ 1 seconde.
d) CDN images et preload
Un CDN n’est pas un luxe, c’est une nécessité. Aliyun OSS coûte quelques dizaines d’euros par an.
<!-- Preload des images critiques — le navigateur les charge en priorité -->
<link rel="preload" as="image" href="hero.jpg">
<!-- CDN images (conversion de format + compression automatiques) -->
<!-- Tencent COS, Aliyun OSS, Cloudflare Images le supportent -->
<img src="https://cdn.example.com/hero.jpg?x-oss-process=image/format,webp/quality,80">
e) Dimensions et compression
Outils recommandés :
- TinyPNG : compression en ligne, simple et efficace
- ImageOptim : incontournable sur Mac, compression par lot
- Squoosh : de Google, support AVIF
Règles de compression : - Images above-the-fold : qualité 80 % (différence visuelle minime)
- Autres images : 70 % (largement suffisant)
- Largeur : double de la maquette (écrans haute densité)
f) Éviter les gros Base64 inline
// ❌ Ne pas inline les grosses images (plus de 10 Ko)
// Augmente le volume HTML et retarde le rendu
const heroImage = 'data:image/jpeg;base64,/9j/4AAQSkZJRg...' // 500 Ko
// ✅ Base64 seulement pour petites icônes (moins de 10 Ko)
// ex. icône de chargement, SVG simples
const icon = 'data:image/svg+xml;base64,PHN2ZyB3aWR...' // 2 Ko
2. Optimiser le temps de réponse serveur (ROI : ⭐⭐⭐⭐)
a) Utiliser un CDN
Toutes les ressources statiques via CDN ; le HTML peut aussi être mis en cache (avec SSG/ISR). J’ai vu des TTFB passer de 200 ms à 50 ms — la différence se ressent immédiatement.
b) Rendu serveur (SSR) ou génération statique (SSG)
// Next.js — génération statique (préféré, meilleures perfs)
export async function getStaticProps() {
const data = await fetchData()
return {
props: { data },
revalidate: 60 // ISR : régénération toutes les 60 secondes
}
}
// Ou rendu serveur (si les données doivent être en temps réel)
export async function getServerSideProps() {
const data = await fetchData()
return { props: { data } }
}
c) Optimisation des requêtes base de données
- Ajouter des index (n’oubliez pas EXPLAIN)
- Redis pour les données chaudes
- Éviter les requêtes N+1 (JOIN ou DataLoader)
3. Optimisation du chargement des ressources (ROI : ⭐⭐⭐)
a) CSS critique inline
<!-- CSS critique above-the-fold inline dans <head> -->
<style>
.hero {
width: 100%;
height: 600px;
background: #f0f0f0;
}
.nav {
position: fixed;
top: 0;
width: 100%;
}
</style>
<!-- CSS non critique différé -->
<link rel="preload" href="styles.css" as="style" onload="this.onload=null;this.rel='stylesheet'">
<noscript><link rel="stylesheet" href="styles.css"></noscript>
b) Optimisation des polices
/* font-display contre le FOIT (Flash of Invisible Text) */
@font-face {
font-family: 'CustomFont';
src: url('font.woff2') format('woff2');
font-display: swap; /* Afficher la police de secours tout de suite */
}
<!-- Preload des polices critiques -->
<link rel="preload" href="font.woff2" as="font" type="font/woff2" crossorigin>
Chapitre 3 : Optimisation INP — des interactions plus fluides
Les scripts tiers sont le pire ennemi de l’INP ! Cas extrême : une page avec 12 scripts tiers — Google Analytics, Facebook Pixel, widget support, scripts pub… INP directement au-dessus de 800 ms.
1. Optimiser l’exécution JavaScript (ROI : ⭐⭐⭐⭐)
a) Code splitting
Le code splitting, ce n’est pas si compliqué : charger « à la demande » — le code d’une page n’est chargé que quand l’utilisateur y va.
// React — code splitting par route
import { lazy, Suspense } from 'react'
const Dashboard = lazy(() => import('./Dashboard'))
const Profile = lazy(() => import('./Profile'))
function App() {
return (
<Suspense fallback={<div>Loading...</div>}>
<Routes>
<Route path="/dashboard" element={<Dashboard />} />
<Route path="/profile" element={<Profile />} />
</Routes>
</Suspense>
)
}
// Vue 3 — composants asynchrones
const Dashboard = defineAsyncComponent(() => import('./Dashboard.vue'))
Effet réel : back-office admin de 1,2 Mo à 200 Ko au first view, INP de 450 ms à 180 ms — ouverture plus de deux fois plus rapide.
b) Découper les longues tâches
Si le thread principal est bloqué plus de 50 ms, c’est une long task — l’INP grimpe.
// ❌ Long task qui bloque le thread principal
function processLargeData(data) {
for (let i = 0; i < 10000; i++) {
// Calcul lourd, 10 000 entrées d'un coup
heavyCalculation(data[i])
}
}
// ✅ Découper avec requestIdleCallback
function processLargeData(data) {
let index = 0
function processChunk() {
const chunkSize = 100 // 100 entrées à la fois
let count = 0
while (index < data.length && count < chunkSize) {
heavyCalculation(data[index])
index++
count++
}
if (index < data.length) {
// Pas terminé — continuer au prochain idle
requestIdleCallback(processChunk)
}
}
requestIdleCallback(processChunk)
}
c) Utiliser des Web Workers
Déporter les calculs lourds dans un Worker, libérer le thread principal.
// worker.js
self.onmessage = (e) => {
const result = complexCalculation(e.data) // calcul dans le Worker
self.postMessage(result)
}
// main.js
const worker = new Worker('worker.js')
worker.postMessage(data)
worker.onmessage = (e) => {
console.log('Result:', e.data)
// Mettre à jour l'UI avec le résultat
}
2. Optimiser les scripts tiers (ROI : ⭐⭐⭐⭐⭐)
Les scripts tiers, c’est comme des invités à une fête — plus il y en a, plus c’est le chaos. Chacun veut des ressources.
a) Différer les scripts non critiques
<!-- ❌ Chargement bloquant (fige le rendu) -->
<script src="analytics.js"></script>
<!-- ✅ Après le chargement de la page -->
<script defer src="analytics.js"></script>
<!-- ✅ Ou délai manuel de 3 secondes (plus agressif) -->
<script>
window.addEventListener('load', () => {
setTimeout(() => {
const script = document.createElement('script')
script.src = 'analytics.js'
document.body.appendChild(script)
}, 3000) // Analytics après 3 s de navigation
})
</script>
b) Pattern Facade pour composants lourds
Un embed YouTube pèse plus de 1 Mo — le charger directement plombe l’INP.
// Miniature YouTube légère, vidéo réelle au clic
function VideoFacade({ videoId }) {
const [showVideo, setShowVideo] = useState(false)
if (!showVideo) {
return (
<div
className="video-facade"
style={{
backgroundImage: `url(https://i.ytimg.com/vi/${videoId}/maxresdefault.jpg)`,
cursor: 'pointer'
}}
onClick={() => setShowVideo(true)}
>
<button className="play-button">▶ Lire la vidéo</button>
</div>
)
}
return <iframe src={`https://www.youtube.com/embed/${videoId}`} />
}
Effet réel : suppression de l’embed YouTube auto, INP de 650 ms à 220 ms.
3. Optimiser la gestion des événements (ROI : ⭐⭐⭐)
a) Debounce et throttle
import { debounce, throttle } from 'lodash'
// Debounce pour la recherche (300 ms après la dernière frappe)
const handleSearch = debounce((value) => {
fetchSearchResults(value)
}, 300)
// Throttle pour le scroll (max toutes les 100 ms)
const handleScroll = throttle(() => {
updateScrollPosition()
}, 100)
b) Écouteurs passifs
// Meilleures perfs de scroll — le navigateur sait que preventDefault ne sera pas appelé
window.addEventListener('scroll', handleScroll, { passive: true })
window.addEventListener('touchmove', handleTouch, { passive: true })
Chapitre 4 : Optimisation CLS — éviter les sauts de layout
Le CLS, c’est comme quelqu’un qui pousse votre livre vers le haut pendant que vous lisez — vous devez retrouver la ligne. Bonne nouvelle : c’est la métrique la plus facile à corriger.
1. Dimensions pour images et vidéos (ROI : ⭐⭐⭐⭐⭐)
Images sans dimensions : première cause de CLS — et la plus simple à corriger.
<!-- ❌ Sans dimensions — le layout saute au chargement -->
<img src="photo.jpg" alt="Photo">
<!-- ✅ Avec attributs — le navigateur réserve l'espace à l'avance -->
<img src="photo.jpg" width="800" height="600" alt="Photo">
<!-- ✅ Ou CSS aspect-ratio (navigateurs modernes) -->
<style>
img {
width: 100%;
aspect-ratio: 16 / 9;
}
</style>
Cas réel : site d’actualités — dimensions sur toutes les images, CLS de 0,35 à 0,05. aussi simple que ça.
2. Optimiser le chargement des polices (ROI : ⭐⭐⭐⭐)
a) Utiliser font-display: swap
@font-face {
font-family: 'CustomFont';
src: url('font.woff2') format('woff2');
font-display: swap; /* Éviter le FOIT (écran blanc pendant le chargement) */
}
b) Preload des polices critiques
<link rel="preload" href="font.woff2" as="font" type="font/woff2" crossorigin>
c) Polices système ou Variable Font
/* Polices système — pas de téléchargement, latence nulle */
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', sans-serif;
/* Variable Font — un fichier pour tous les graisses (100–900) */
@font-face {
font-family: 'Inter';
src: url('Inter-Variable.woff2') format('woff2-variations');
font-weight: 100 900;
}
3. Réserver l’espace pour le contenu dynamique (ROI : ⭐⭐⭐⭐)
a) Espace réservé pour la publicité
.ad-container {
min-height: 250px; /* Hauteur pub réservée — pas de saut avant chargement */
background: #f0f0f0; /* Fond placeholder */
}
b) Skeleton screen
Les skeletons ne servent pas qu’à « faire joli » — ils stabilisent le layout.
// Afficher un skeleton avant chargement — pas d'apparition brutale
function ProductCard({ loading, data }) {
if (loading) {
return (
<div className="skeleton">
<div className="skeleton-image" style={{ width: '100%', height: '200px', background: '#e0e0e0' }} />
<div className="skeleton-title" style={{ width: '80%', height: '20px', background: '#e0e0e0', margin: '10px 0' }} />
<div className="skeleton-price" style={{ width: '40%', height: '20px', background: '#e0e0e0' }} />
</div>
)
}
return (
<div className="product">
<img src={data.image} alt={data.title} />
<h3>{data.title}</h3>
<p>{data.price}</p>
</div>
)
}
4. Ne pas insérer de contenu au-dessus de l’existant (ROI : ⭐⭐⭐⭐⭐)
// ❌ Banner en haut — le contenu descend (CLS explose)
<div>
{showBanner && <Banner />}
<Content />
</div>
// ✅ position fixed — le layout reste stable
<div>
{showBanner && <Banner style={{ position: 'fixed', top: 0, zIndex: 1000 }} />}
<Content style={{ marginTop: showBanner ? '60px' : '0' }} />
</div>
5. Animer avec transform plutôt que top/left (ROI : ⭐⭐⭐)
J’ai vu des animations avec top pour « faire joli » — le CLS a explosé.
/* ❌ Déclenche un layout, CLS en hausse */
.element {
position: relative;
animation: slideIn 0.3s;
}
@keyframes slideIn {
from { top: -100px; } /* top déclenche un reflow */
to { top: 0; }
}
/* ✅ transform — composite uniquement (accélération GPU) */
.element {
animation: slideIn 0.3s;
}
@keyframes slideIn {
from { transform: translateY(-100px); }
to { transform: translateY(0); }
}
Chapitre 5 : Mise en pratique — processus d’optimisation complet
Les priorités comptent — ne commencez pas par le SSR, optimisez d’abord les images. Lors de ma première passe, ajouter width/height aux images a seul rapporté 10 points — des points gratuits.
Matrice des priorités
| Optimisation | ROI | Difficulté | Priorité |
|---|---|---|---|
| Dimensions des images | Très élevé | Très faible | P0 |
| Conversion de format | Très élevé | Faible | P0 |
| Images LCP | Très élevé | Faible | P0 |
| Scripts tiers différés | Très élevé | Faible | P0 |
| Polices | Élevé | Faible | P1 |
| Code splitting | Élevé | Moyen | P1 |
| CDN | Élevé | Faible | P1 |
| SSR/SSG | Moyen | Élevé | P2 |
| Web Workers | Faible | Élevé | P3 |
Outils et commandes de mesure
# 1. Lighthouse (référence)
# Chrome DevTools > Lighthouse > Generate report
# Mode navigation privée — les extensions faussent le score
# 2. WebPageTest (appareils réels)
# https://webpagetest.org
# Régions et vitesses réseau configurables
# 3. Panneau Performance Chrome DevTools
# Enregistrer le chargement, analyser les goulots
# Durée de chaque tâche visible
# 4. Analyse des paquets npm
npx webpack-bundle-analyzer
# Visualise le plus gros bundle
# 5. Analyse d'images
npx sharp-cli info image.jpg
# Dimensions, format et taille du fichier
Pièges à éviter
- ❌ Ne pas tester Lighthouse en production (navigation privée ou extensions désactivées)
- ❌ Ne pas tester une seule fois (au moins 3 runs, moyenne — le réseau fluctue)
- ❌ Ne pas tester uniquement sur desktop (mobile plus important — 75 % du trafic)
- ❌ Ne pas ignorer le Network throttling (simuler un réseau lent, ex. Fast 3G)
- ❌ Ne jamais lazy-loader les images LCP (piège que j’ai vécu)
Conclusion
L’optimisation des performances n’est pas un one-shot, c’est un processus continu — comme le sport, ça demande de la régularité. Voir Lighthouse passer de 60 à 90, c’est une vraie satisfaction.
Ce n’est pas que de la technique : c’est du respect envers l’utilisateur. Chaque seconde gagnée retient plus de visiteurs. Sur mobile, la patience tient souvent 3 secondes — et vous pouvez réduire ces 3 secondes à 1.
Ensemble, offrons à chaque visiteur une navigation fluide.
FAQ
Quels sont les seuils des trois Core Web Vitals ?
• Moins de 2,5 s : bon, 2,5–4 s : à améliorer, plus de 4 s : mauvais
INP (Interaction to Next Paint) :
• Moins de 200 ms : bon, 200–500 ms : à améliorer, plus de 500 ms : mauvais
• Depuis mars 2024, l'INP remplace le FID comme métrique centrale
CLS (Cumulative Layout Shift) :
• Moins de 0,1 : bon, 0,1–0,25 : à améliorer, plus de 0,25 : mauvais
Données clés :
• Temps de chargement de 3 à 5 s : taux de rebond +38 %
• Mobile > 3 s : 53 % des utilisateurs partent
• LCP optimisé : conversion +7 à 15 %
Comment optimiser le LCP (Largest Contentful Paint) ?
1) Formats modernes (AVIF +41 % vs JPEG, WebP +30 %)
• Cas réel : hero de 500 Ko à 120 Ko, LCP de 4,2 s à 2,1 s
2) Images LCP avec priority, pas de lazy loading — eager above-the-fold
3) srcset responsif économise jusqu'à 60 % de bande passante
4) CDN et preload des images critiques
5) Temps de réponse serveur (CDN, SSR/SSG, requêtes BDD)
Priorité : optimisation images = ROI maximal, effort faible, effet rapide.
Comment optimiser l'INP (Interaction to Next Paint) ?
1) Code splitting par route (React lazy, Vue defineAsyncComponent)
• Cas réel : 1,2 Mo → 200 Ko au first view, INP 450 ms → 180 ms
2) Scripts tiers différés (defer ou délai 3 s), pattern Facade pour composants lourds
3) Supprimer les embeds auto (ex. YouTube : INP 650 ms → 220 ms)
4) Découper les longues tâches avec requestIdleCallback
5) Web Workers pour calculs complexes
6) Debounce, throttle et écouteurs passifs
Comment optimiser le CLS (Cumulative Layout Shift) ?
1) Dimensions sur les images (width/height ou CSS aspect-ratio)
• Cas réel : CLS 0,35 → 0,05
2) Polices (font-display: swap, preload, polices système ou Variable Font)
3) Espace réservé (min-height pub, skeleton screens)
4) Ne pas insérer au-dessus du contenu existant (position fixed)
5) Animer avec transform plutôt que top/left (composite, pas layout)
Quelles sont les priorités d'optimisation des performances ?
• Dimensions des images
• Conversion de format (AVIF/WebP)
• Images LCP (priority, pas de lazy loading)
• Scripts tiers différés
P1 (ROI élevé) :
• Polices (font-display: swap)
• Code splitting
• CDN
P2 (ROI moyen, effort élevé) :
• SSR/SSG
P3 (ROI faible, effort élevé) :
• Web Workers
Conseil : commencer par P0 — seules les dimensions images rapportent facilement 10 points. Ne pas attaquer le SSR avant les images.
Peut-on lazy-loader les images LCP ?
Les images LCP (hero above-the-fold) doivent avoir priority ou loading="eager", pas de lazy loading. Le lazy loading sert aux images sous la ligne de flottaison.
Erreur :
<img src="hero.jpg" loading="lazy"> — le score LCP baisse
Correct :
• eager above-the-fold, lazy en dessous
• Aucune image visible au premier écran ne doit être lazy-loadée
Comment mesurer et tester les Core Web Vitals ?
1) Lighthouse (Chrome DevTools, référence — navigation privée contre les extensions)
2) WebPageTest (appareils réels, régions et vitesses réseau)
3) Panneau Performance Chrome DevTools (enregistrer le chargement, trouver les goulots)
4) webpack-bundle-analyzer (visualiser les plus gros bundles)
5) sharp-cli info (dimensions, format, taille des images)
Pièges :
• Ne pas tester en production
• Ne pas mesurer une seule fois (≥ 3 runs, moyenne)
• Ne pas tester desktop seul (mobile : 75 % du trafic)
• Ne pas ignorer le Network throttling (réseau lent simulé)
14 min de lecture · Publié le: 24 nov. 2025 · Mis à jour le: 27 juil. 2026
Framework frontend
Si vous arrivez depuis la recherche, le plus rapide est de passer à l’article précédent ou suivant de cette série.
Précédent
Vue 3 + TypeScript : bonnes pratiques et architecture entreprise en 2025
Guide complet d'architecture Vue 3 + TypeScript pour projets entreprise en 2025 : initialisation Vite, Pinia, typage TypeScript, ESLint 9 et configurations prêtes à l'emploi.
Partie 5 sur 6
Suivant
C’est le dernier article publié dans cette série pour le moment.



Commentaires
Connectez-vous avec GitHub pour laisser un commentaire