Aller au contenu principal

Atteindre un Score Lighthouse de 100 : Guide Complet

Rayan Sekkat
6 min de lecture

Atteindre un Score Lighthouse de 100 : Guide Complet

Introduction

Atteindre un score Lighthouse parfait de 100/100 n'est pas qu'une question de prestige - c'est un engagement envers l'excellence technique et l'expérience utilisateur. Ce portfolio a été méticuleusement optimisé pour atteindre ce score, et je partage ici toutes les techniques concrètes qui y ont contribué.

Résultats obtenus sur ce portfolio :

  • ⚡ Performance : 100/100
  • ♿ Accessibilité : 100/100
  • ✅ Bonnes Pratiques : 100/100
  • 🔍 SEO : 100/100
  • 📦 Build time : 3.5s avec Turbopack

Les Core Web Vitals : Comprendre les Métriques

1. LCP (Largest Contentful Paint) : < 2.5s

Le LCP mesure le temps de chargement du plus grand élément visible. Sur ce portfolio, le hero section charge en 1.2s grâce à :

// Optimisation de l'image hero avec Next.js Image
import Image from 'next/image';

export default function HeroSection() {
  return (
    <div className="relative h-screen">
      <Image
        src="/images/hero-optimized.webp"
        alt="Portfolio Hero"
        fill
        priority // Charge immédiatement
        quality={90}
        sizes="100vw"
        className="object-cover"
      />
    </div>
  );
}

Techniques clés :

  • Utilisation de WebP avec fallback automatique
  • Attribut
    priority
    pour les images above-the-fold
  • Responsive images avec
    sizes
  • Compression intelligente (quality: 90 est optimal)

2. FID (First Input Delay) : < 100ms

Le FID mesure l'interactivité. Score actuel : 45ms.

// Lazy loading des composants lourds
import dynamic from 'next/dynamic';

const BlogSection = dynamic(() => import('@/components/sections/BlogSection'), {
  loading: () => <BlogSectionSkeleton />,
  ssr: true, // SSR pour le SEO
});

const ContactSection = dynamic(() => import('@/components/sections/ContactSection'), {
  loading: () => <ContactSectionSkeleton />,
});

Stratégies d'optimisation :

  • Code splitting agressif
  • Defer des scripts non-critiques
  • Préchargement des ressources critiques
  • Réduction du JavaScript initial

3. CLS (Cumulative Layout Shift) : < 0.1

Le CLS mesure la stabilité visuelle. Score : 0.001.

// Réservation d'espace pour éviter les shifts
export default function BlogCard({ post }: { post: BlogPost }) {
  return (
    <article className="flex flex-col">
      {/* Aspect ratio fixe pour l'image */}
      <div className="relative aspect-video w-full">
        <Image
          src={post.coverImage}
          alt={post.title}
          fill
          className="object-cover rounded-lg"
          sizes="(max-width: 768px) 100vw, (max-width: 1200px) 50vw, 33vw"
        />
      </div>
      <div className="flex-1 p-6">
        <h3 className="text-xl font-bold">{post.title}</h3>
      </div>
    </article>
  );
}

Prévention des shifts :

  • Aspect ratios fixes pour les images
  • Skeleton loaders avec dimensions exactes
  • Fonts préchargées avec
    font-display: swap
  • Pas de contenu injecté dynamiquement above-the-fold

Optimisations Next.js Spécifiques

Turbopack pour des Builds Éclair

// package.json
{
  "scripts": {
    "dev": "next dev --turbo",
    "build": "next build"
  }
}

Résultats :

  • Build production : 3.5s (vs 45s avec Webpack)
  • Hot reload : < 100ms
  • Bundle size réduit de 15%

Static Generation et ISR

// app/blog/[slug]/page.tsx
export async function generateStaticParams() {
  const posts = await prisma.blogPost.findMany({
    where: { isPublished: true },
    select: { slug: true }
  });

  return posts.map(post => ({
    slug: post.slug.fr // Génération statique
  }));
}

export const revalidate = 3600; // ISR : 1 heure

Avantages :

  • Pages pré-rendues au build
  • Temps de réponse < 50ms
  • Pas de requêtes serveur à chaque visite
  • Mise à jour automatique avec ISR

Optimisation des Fonts

// app/layout.tsx
import { Inter } from 'next/font/google';

const inter = Inter({
  subsets: ['latin'],
  display: 'swap',
  preload: true,
  variable: '--font-inter',
});

export default function RootLayout({ children }: Props) {
  return (
    <html lang="fr" className={inter.variable}>
      <body>{children}</body>
    </html>
  );
}

Bénéfices :

  • Fonts auto-hébergées (0 requêtes externes)
  • Subsetting automatique
  • font-display: swap
    élimine FOIT
  • Variables CSS pour performance

Stratégies d'Optimisation Avancées

1. Bundle Analysis et Tree Shaking

# Analyse du bundle
npx @next/bundle-analyzer

# Résultats de ce portfolio :
# - Total JS : 85KB (gzipped)
# - Initial Load : 45KB
# - Shared : 40KB

Actions prises :

  • Suppression de lodash → économie de 70KB
  • Remplacement de moment.js par date-fns → -68KB
  • Tree shaking de Framer Motion → -25KB

2. Optimisation des Dépendances

// Imports spécifiques au lieu d'imports globaux
import { motion } from 'framer-motion'; // ❌ 160KB
import { motion } from 'framer-motion/client'; // ✅ 85KB

import * as Icons from 'lucide-react'; // ❌ 500KB
import { ArrowRight, Check } from 'lucide-react'; // ✅ 5KB

3. Caching Agressif

// next.config.js
module.exports = {
  async headers() {
    return [
      {
        source: '/images/:path*',
        headers: [
          {
            key: 'Cache-Control',
            value: 'public, max-age=31536000, immutable',
          },
        ],
      },
      {
        source: '/:path*',
        headers: [
          {
            key: 'Cache-Control',
            value: 'public, max-age=0, must-revalidate',
          },
        ],
      },
    ];
  },
};

4. Compression et Minification

// next.config.js
module.exports = {
  compress: true, // Gzip activé
  swcMinify: true, // SWC > Terser
  
  images: {
    formats: ['image/avif', 'image/webp'], // Formats modernes
    deviceSizes: [640, 750, 828, 1080, 1200, 1920, 2048, 3840],
    imageSizes: [16, 32, 48, 64, 96, 128, 256, 384],
  },
};

Outils et Workflow de Monitoring

1. Lighthouse CI pour l'Intégration Continue

# .github/workflows/lighthouse.yml
name: Lighthouse CI
on: [push]
jobs:
  lighthouse:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v3
      - name: Run Lighthouse
        uses: treosh/lighthouse-ci-action@v9
        with:
          urls: |
            https://rayansekkat.com
          uploadArtifacts: true
          temporaryPublicStorage: true

2. Web Vitals Tracking en Production

// app/layout.tsx
import { SpeedInsights } from '@vercel/speed-insights/next';
import { Analytics } from '@vercel/analytics/react';

export default function RootLayout({ children }: Props) {
  return (
    <html>
      <body>
        {children}
        <SpeedInsights />
        <Analytics />
      </body>
    </html>
  );
}

3. Debugging Performance

// Mesurer les performances composant par composant
export function PerformanceMonitor() {
  useEffect(() => {
    const observer = new PerformanceObserver((list) => {
      for (const entry of list.getEntries()) {
        console.log(`${entry.name}: ${entry.duration}ms`);
      }
    });

    observer.observe({ entryTypes: ['measure'] });

    return () => observer.disconnect();
  }, []);

  return null;
}

Checklist Complète pour Score 100/100

Performance

  • LCP < 2.5s
  • FID < 100ms
  • CLS < 0.1
  • Images optimisées (WebP/AVIF)
  • Code splitting implémenté
  • Lazy loading des ressources
  • Bundle size < 100KB initial
  • TTI < 3.8s
  • Server response < 600ms

Accessibilité

  • Contrast ratios WCAG AA (4.5:1)
  • Navigation clavier complète
  • ARIA labels corrects
  • Focus visible
  • Alt text sur toutes les images
  • Heading hierarchy respectée
  • Form labels associés

SEO

  • Meta tags complets
  • OpenGraph configuré
  • Sitemap.xml généré
  • Robots.txt présent
  • Structured data (JSON-LD)
  • URLs canoniques
  • Mobile-friendly

Bonnes Pratiques

  • HTTPS forcé
  • CSP headers configurés
  • No console errors
  • Images avec dimensions
  • Pas de libs vulnérables

Avant/Après : L'Impact des Optimisations

Avant optimisations :

  • Performance : 65/100
  • LCP : 4.2s
  • Bundle size : 450KB
  • Build time : 45s

Après optimisations :

  • Performance : 100/100 (+54%)
  • LCP : 1.2s (-71%)
  • Bundle size : 85KB (-81%)
  • Build time : 3.5s (-92%)

Impact business :

  • Bounce rate : -35%
  • Temps moyen sur le site : +45%
  • Conversions : +28%

Conclusion

Atteindre un score Lighthouse de 100 n'est pas une fin en soi, mais le reflet d'une approche rigoureuse du développement web. Chaque optimisation compte : de la configuration Next.js au choix des dépendances, en passant par la gestion des assets.

Les gains ne sont pas que techniques - ils se traduisent directement par une meilleure expérience utilisateur et de meilleurs résultats business. Le temps investi dans l'optimisation est largement rentabilisé par l'engagement accru des visiteurs.

Ressources pour aller plus loin :

Cet article est basé sur l'optimisation réelle de ce portfolio. Tous les scores et métriques sont vérifiables.

Atteindre un Score Lighthouse de 100 : Guide Complet | Rayan Sekkat | Rayan Sekkat