Esc
 Naviguer  Ouvrir Esc Fermer
Aller au contenu

13.1 — Mesurer avant d'optimiser

🎯 Objectif : ne jamais optimiser sans mesure préalable. Apprendre les 3 indicateurs qui comptent (LCP, INP, CLS), les outils pour les obtenir, et la différence entre labo (synthétique) et terrain (RUM).

À l'issue de cet axe, tu sauras :

  • Comprendre LCP, INP et CLS — les Core Web Vitals 2026
  • Lire un rapport Lighthouse sans s'arrêter au score global
  • Utiliser Chrome DevTools Performance pour profiler une page
  • Mettre en place du Real User Monitoring (RUM) avec web-vitals.js
  • Choisir entre mesures labo et terrain selon le contexte

Confirmé 10 min prérequis : axes 5-9 lus

« Premature optimization is the root of all evil. » — Donald Knuth

Optimiser sans mesurer, c’est :

  • Perdre du temps sur ce qui n’est pas le bottleneck.
  • Casser des choses qui marchent (un cache mal placé crée des bugs subtils).
  • Ne pas savoir si ça a marché — pas de mesure « avant », pas de comparaison.

La séquence saine est toujours la même :

  1. Mesurer → identifier la métrique qui pose problème.
  2. Hypothèse → quelle cause probable ?
  3. Modifier une seule chose à la fois.
  4. Re-mesurer → comparer avant/après.
  5. Conserver ou annuler.

Google Core Web Vitals : 3 métriques user-centric standardisées qui pèsent dans le SEO et reflètent la perception réelle de la performance.

VitalMesureBonÀ améliorerMauvais
LCP (Largest Contentful Paint)Temps avant le plus gros élément visible≤ 2,5 s≤ 4,0 s> 4,0 s
INP (Interaction to Next Paint)Latence ressentie sur les interactions≤ 200 ms≤ 500 ms> 500 ms
CLS (Cumulative Layout Shift)Décalages de mise en page imprévus≤ 0,1≤ 0,25> 0,25

INP a remplacé FID en mars 2024. INP regarde toutes les interactions (clic, tap, clavier), pas seulement la première. Beaucoup plus exigeant en pratique.

timeline
    title Cycle de vie d'une page
    Chargement   : LCP — Largest Contentful Paint
    Interactivité : INP — Interaction to Next Paint
    Stabilité    : CLS — Cumulative Layout Shift
Les 3 Core Web Vitals — chargement, interactivité, stabilité

Le LCP mesure quand l’élément le plus gros visible (image hero, gros bloc de texte, vidéo) finit de s’afficher. C’est ce qui te fait dire « la page a chargé ».

Bottlenecks classiques :

CauseSymptômePiste
Image hero trop lourdeLCP > 4 ssrcset, AVIF/WebP, fetchpriority="high"
Police bloquanteTexte invisible 2-3 sfont-display: swap, preload
JS qui retarde le renduLCP retardédéfer le JS non critique
Serveur lent (TTFB > 600 ms)LCP plafonnecache, edge, DB
Render-blocking CSSFirst Paint tardinliner le CSS critique

INP mesure la latence d’interaction. Quand tu cliques sur un bouton, combien de temps avant que le prochain frame visible reflète l’action ?

Bottlenecks classiques :

  • Long tasks sur le main thread (> 50 ms) qui bloquent le clic.
  • Re-rendus React/Vue trop coûteux sur chaque keystroke.
  • Hydratation lourde d’une page SSR au premier clic.
  • Listeners non passifs sur scroll/touch.

CLS additionne tous les décalages inattendus au cours de la session. Une bannière qui pousse le contenu vers le bas après chargement = utilisateur qui clique sur la mauvaise chose.

Bottlenecks classiques :

CauseCorrectif
Image sans width / heightToujours définir les dimensions ou un aspect-ratio
Police qui change la métrique au swapsize-adjust, ascent-override (font fallback métriquement compatible)
Bannière cookies qui pousseposition: fixed ou réserver l’espace
Pub injectée dynamiquementConteneur avec hauteur fixe
Animation top/left au lieu de transformtransform ne déclenche pas de layout

Selon le contexte, regarde aussi :

MétriqueSensBon à savoir
TTFB (Time To First Byte)Latence serveurLCP plafonne au TTFB. < 600 ms idéal.
FCP (First Contentful Paint)Premier pixel renduBon proxy pour « ça commence à s’afficher ».
TBT (Total Blocking Time)Somme des long tasks pendant le chargementProxy labo de l’INP futur.
Speed IndexVitesse perçue d’apparitionBon pour comparer 2 versions.
Hydration TimeCombien de temps avant interactifPertinent SSR/Next.js/Astro.

Tests synthétiques : exécutés depuis un environnement contrôlé (CPU bridé, réseau simulé). Reproductibles, rapides, idéaux en CI.

OutilUsage
Lighthouse (DevTools / CLI / CI)Audit complet rapide, score perf + a11y + best practices + SEO
PageSpeed InsightsLighthouse + données CrUX terrain agrégées par Google
WebPageTestMulti-locations, multi-réseaux, waterfall détaillé
Chrome DevTools PerformanceProfil détaillé : main thread, layers, paints
Bundle Analyzer (webpack-bundle-analyzer, vite-bundle-visualizer, @next/bundle-analyzer)Identifier les gros poids JS

Ce que vivent tes vrais utilisateurs, sur leurs vrais devices. Les meilleurs outils :

OutilForce
web-vitals.js3 KB, gratuit, capture LCP/INP/CLS et envoie à ton backend
Google CrUXDonnées terrain agrégées par Chrome (gratuit, dispo via PSI / Search Console)
Vercel Analytics / Cloudflare Web AnalyticsRUM clé en main, sans impact perf
Sentry Performance / Datadog RUMPlus riches (sessions complètes, replays)
PostHogOSS, RUM + product analytics

Exemple minimal avec web-vitals :

import { onLCP, onINP, onCLS } from 'web-vitals';
function send(metric: { name: string; value: number; rating: string }) {
navigator.sendBeacon('/rum', JSON.stringify(metric));
}
onLCP(send);
onINP(send);
onCLS(send);

navigator.sendBeacon est non-bloquant et survit au pagehide — parfait pour ne pas perdre les mesures à la fermeture de l’onglet.

QuestionLaboTerrain
« Cette PR a-t-elle régressé ? »✅ rapide, reproductible❌ trop lent à observer
« Quels users souffrent le plus ? »❌ univers fictif✅ segmentation device/réseau
« Quel est notre LCP p75 vrai ? »❌ approximation✅ valeur réelle
« Trouver une régression rare (1 % users) »❌ jamais reproduite✅ visible en agrégé

Combinaison saine : Lighthouse en CI (régressions PR) + RUM en prod (réalité terrain).


Lire un rapport Lighthouse — ce qui compte vraiment

Section intitulée « Lire un rapport Lighthouse — ce qui compte vraiment »

Hiérarchie des sections, par ordre d’importance pour la perf :

  1. Métriques — LCP, INP (proxy TBT), CLS, FCP, TTFB.
  2. Diagnostics — opportunités (combien gagnerait-on à faire X ?).
  3. Treemap des bundles JS et CSS (où est le poids ?).
  4. Trace (l’onglet Performance dans DevTools) pour creuser.

Les opportunités Lighthouse les plus actionnables :

  • Eliminate render-blocking resources → CSS/JS chargés trop tôt.
  • Properly size images → tu sers du 4000×3000 pour un slot de 800 px.
  • Defer offscreen imagesloading="lazy".
  • Avoid enormous network payloads → bundle JS > 500 KB compressé.
  • Reduce unused JavaScript → tree-shaking insuffisant, dépendances trop grosses.
  • Avoid long main-thread tasks → script qui bloque > 50 ms.

Workflow standard pour traquer un problème :

  1. Onglet Performance → bouton record (Ctrl+E).
  2. Reproduis le scénario lent (clic, scroll, navigation).
  3. Stop → DevTools t’affiche un flame chart.
  4. Cherche les barres rouges (long tasks) en haut.
  5. Clique dessus → tu vois la stack JavaScript responsable.
  6. Compare Total Blocking Time avant/après ton fix.

Bloque les régressions à la PR avec Lighthouse CI :

.github/workflows/lighthouse.yml
on: [pull_request]
jobs:
lhci:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with: { node-version: 24 }
- run: npm ci && npm run build
- run: npx --yes @lhci/cli@0.14.x autorun
env:
LHCI_GITHUB_APP_TOKEN: ${{ secrets.LHCI_GITHUB_APP_TOKEN }}

Et un lighthouserc.json avec des budgets durs :

{
"ci": {
"collect": { "url": ["http://localhost:3000/"], "numberOfRuns": 3 },
"assert": {
"assertions": {
"largest-contentful-paint": ["error", { "maxNumericValue": 2500 }],
"cumulative-layout-shift": ["error", { "maxNumericValue": 0.1 }],
"total-blocking-time": ["error", { "maxNumericValue": 200 }],
"categories:performance": ["warn", { "minScore": 0.9 }]
}
}
}
}

Une PR qui dépasse ces budgets ne mergera pas. C’est l’équivalent perf de tes tests unitaires.


Tu changes une image hero pour un format AVIF, son poids passe de 1,2 MB à 180 KB. Quel Core Web Vital va le plus probablement s'améliorer ?
Ton score Lighthouse est de 95/100 mais Sentry RUM remonte un INP p75 de 480 ms en production. Comment réconcilier ?
Tu observes un CLS de 0,42 sur la page d'accueil. Lequel est la cause LA PLUS probable ?


Suite : 13.2 — Optimisations frontend pour les leviers concrets côté navigateur.