💻 Développement

dev-web-performance-optimizer

Optimise les performances web (Core Web Vitals, loading, rendering).

⚡ Installation & lancement en 1 commande

Copiez-collez dans votre terminal : le skill s'installe dans ~/.claude/skills et Claude Code se lance directement dessus.

macOS / Linux
curl -fsSL https://raw.githubusercontent.com/khalilbenaz/claude-skills-collection/main/install.sh | sh -s -- dev-web-performance-optimizer --launch
Windows (PowerShell)
iex "& { $(iwr -useb https://raw.githubusercontent.com/khalilbenaz/claude-skills-collection/main/install.ps1) } dev-web-performance-optimizer -Launch"

🚀 Déjà installé ?

claude "/dev-web-performance-optimizer"

Ou tapez /dev-web-performance-optimizer dans une session Claude Code, ou décrivez simplement votre besoin — le skill se déclenche automatiquement via le skill-router.

🔑 Déclencheurs automatiques

Le skill s'active automatiquement quand votre demande contient :

performance webCore Web VitalsLCPFIDCLSlighthouse scorepage speedsite lentbundle sizelazy loading

📦 Installation manuelle

git clone https://github.com/khalilbenaz/claude-skills-collection.git cp -r claude-skills-collection/skills/dev-web-performance-optimizer ~/.claude/skills/

Payload du plugin : skills/dev-web-performance-optimizer · source éditable : dev-skills/web-performance-optimizer

đź“– Manuel

Web Performance Optimizer

Étape 1 — Mesurer d'abord

Règle absolue : jamais d'optimisation sans baseline mesurée.

# CLI Lighthouse (headless)
npx lighthouse https://example.com --output=json --output-path=./lh-report.json \
  --form-factor=mobile --throttling-method=simulate

# CrUX field data (données réelles utilisateurs)
npx crux-api --origin https://example.com --key YOUR_API_KEY

# WebPageTest en CLI
npx webpagetest test https://example.com --key YOUR_KEY --location ec2-eu-west-1

Collecter avant et après chaque changement. Utiliser CrUX (données terrain) et non seulement Lighthouse (labo).


Étape 2 — Diagnostiquer par métrique

Seuils "Good" 2026

MétriqueGoodNeeds ImprovementPoor
LCP< 2.5 s2.5 – 4 s> 4 s
INP< 200 ms200 – 500 ms> 500 ms
CLS< 0.10.1 – 0.25> 0.25
TTFB< 800 ms800 ms – 1.8 s> 1.8 s
FCP< 1.8 s1.8 – 3 s> 3 s
FID est remplacé par INP depuis mars 2024. Ne pas cibler FID dans les nouveaux rapports.

Arbre de décision rapide


Étape 3 — Optimiser le chargement des ressources critiques

Images

<!-- LCP image : preload + fetchpriority -->
<link rel="preload" as="image" href="/hero.avif"
      fetchpriority="high" type="image/avif">

<!-- Responsive + lazy hors écran -->
<img src="photo.avif" srcset="photo-400.avif 400w, photo-800.avif 800w"
     sizes="(max-width: 600px) 400px, 800px"
     loading="lazy" decoding="async" width="800" height="600" alt="...">
# Convertir en AVIF/WebP (Sharp CLI)
npx sharp -i input.jpg -o output.avif --format avif --quality 60
npx sharp -i input.jpg -o output.webp --format webp --quality 75

Critères de format : AVIF = meilleure compression (–50 % vs JPEG), mais encodage lent → générer au build. WebP = fallback universel.

Fonts

<!-- Preconnect + preload font critique -->
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link rel="preload" as="font" href="/fonts/inter.woff2"
      type="font/woff2" crossorigin>
/* Éviter FOIT : swap immédiat */
@font-face {
  font-family: "Inter";
  src: url("/fonts/inter.woff2") format("woff2");
  font-display: swap; /* ou 'optional' si CLS est prioritaire */
}

Anti-pattern : font-display: block = invisible jusqu'à 3 s → toujours swap ou optional.


Étape 4 — Optimiser JavaScript (bundle)

# Analyser le bundle webpack
npx webpack-bundle-analyzer stats.json

# Analyser bundle Vite/Rollup
npx vite-bundle-visualizer

# Trouver les dépendances lourdes
npx bundlephobia-cli react-pdf
// Code splitting par route (React / Vue / Svelte)
const Dashboard = lazy(() => import('./Dashboard'));

// Éviter l'import en masse depuis une lib
// ❌ import _ from 'lodash'           // importe 70 KB
// âś…
import debounce from 'lodash/debounce'; // importe 2 KB
<!-- Scripts non critiques : defer ou type="module" -->
<script src="analytics.js" defer></script>
<!-- async uniquement si totalement indépendant du DOM -->
<script src="chat.js" async></script>

Critères : defer = exécuté après parsing HTML ; async = exécuté dès téléchargé (peut bloquer). Préférer defer dans 90 % des cas.


Étape 5 — Réduire le blocage du main thread (INP)

// Découper les long tasks avec scheduler
async function processItems(items) {
  for (const item of items) {
    await scheduler.yield(); // libère le thread régulièrement
    process(item);
  }
}

// Déléguer aux Web Workers
const worker = new Worker('./heavy-computation.js', { type: 'module' });
worker.postMessage({ data: bigArray });
worker.onmessage = ({ data }) => updateUI(data);
// Debouncer les événements fréquents
input.addEventListener('input', debounce(handleSearch, 150));

// Éviter layout thrashing
// ❌
elements.forEach(el => { el.style.width = el.offsetWidth + 'px'; });
// âś…
const widths = elements.map(el => el.offsetWidth);
elements.forEach((el, i) => { el.style.width = widths[i] + 'px'; });

Étape 6 — Corriger le CLS

/* Réserver l'espace pour images et vidéos */
img, video { aspect-ratio: 16 / 9; width: 100%; }

/* Skeleton loader au lieu de contenu dynamique injecté */
.skeleton { background: #e0e0e0; min-height: 200px; border-radius: 4px; }

Causes fréquentes de CLS :


Étape 7 — Cache et livraison

# Cache immuable pour assets versionnés (hash dans le nom de fichier)
location ~* \.(js|css|woff2|avif|webp)$ {
  add_header Cache-Control "public, max-age=31536000, immutable";
}

# Pages HTML : revalidation rapide
location / {
  add_header Cache-Control "public, max-age=0, must-revalidate";
}
// Service Worker : cache-first pour assets statiques
self.addEventListener('fetch', (event) => {
  if (event.request.destination === 'script') {
    event.respondWith(
      caches.match(event.request).then(r => r || fetch(event.request))
    );
  }
});

Checklist infra :


Étape 8 — CSS critique

# Extraire le CSS above-the-fold avec Critical
npx critical index.html --base dist/ --inline --width 1300 --height 900

# Purger le CSS inutilisé
npx purgecss --css dist/styles.css --content dist/**/*.html dist/**/*.js \
  --output dist/styles.purged.css
<!-- Charger le CSS non-critique sans bloquer le rendu -->
<link rel="preload" href="/styles.css" as="style"
      onload="this.onload=null;this.rel='stylesheet'">
<noscript><link rel="stylesheet" href="/styles.css"></noscript>

Anti-patterns et pièges

PiègeProblèmeSolution
Optimiser sans mesurerGains imaginaires, régressions réellesBaseline CrUX/Lighthouse systématique
loading="lazy" sur l'image LCPLCP explose car téléchargement retardéfetchpriority="high" + pas de lazy sur LCP
async sur scripts qui lisent le DOMRace condition, erreurs runtimedefer par défaut
Inliner tout le CSSImpossible Ă  mettre en cacheInliner seulement le CSS critique (< 14 KB)
Compresser en gzip quand Brotli dispo–15 % de ratio perdusBrotli level 6 en production
Ignorer les données CrUX terrainLighthouse labo ≠ expérience réelleToujours valider avec CrUX API ou Search Console
Font font-display: blockFOIT 3 s pour les utilisateurs lentsswap ou optional

Règles de priorité

  1. TTFB d'abord : si > 800 ms, toute optimisation frontend est marginale.
  2. LCP image : précharger, dimensionner, AVIF — gain le plus rapide et le plus visible.
  3. JS inutile : tree shaking + code splitting avant toute micro-optimisation.
  4. Mesurer en terrain : CrUX + RUM (Real User Monitoring) prime sur Lighthouse labo.
  5. Ne pas sur-optimiser : un score Lighthouse 90+ en mobile est une cible saine ; passer de 95 à 100 a peu d'impact réel.