正在加载,请稍候…

Web Performance: Core Web Vitals Optimization Guide

Improve Core Web Vitals (LCP, CLS, FID/INP). Learn image optimization, critical rendering path, code splitting, and monitoring techniques for better user experience.

Web Performance: Core Web Vitals Optimization

Understanding Core Web Vitals

LCP (Largest Contentful Paint) - Loading performance
  Good: < 2.5s, Needs Improvement: 2.5-4s, Poor: > 4s

CLS (Cumulative Layout Shift) - Visual stability
  Good: < 0.1, Needs Improvement: 0.1-0.25, Poor: > 0.25

INP (Interaction to Next Paint) - Responsiveness (replaced FID)
  Good: < 200ms, Needs Improvement: 200-500ms, Poor: > 500ms

LCP Optimization

<!-- Preload LCP image -->
<link rel="preload" as="image" href="/hero.webp" fetchpriority="high">

<!-- Use modern image formats -->
<picture>
  <source srcset="/hero.avif" type="image/avif">
  <source srcset="/hero.webp" type="image/webp">
  <img src="/hero.jpg" alt="Hero" width="1200" height="630" loading="eager">
</picture>
// Next.js: Priority for LCP image
import Image from 'next/image';

<Image
  src="/hero.jpg"
  alt="Hero"
  width={1200}
  height={630}
  priority  // Preloads with fetchpriority="high"
  sizes="100vw"
/>

Image Optimization

// Responsive images with srcset
function ResponsiveImage({ src, alt }: { src: string; alt: string }) {
  return (
    <img
      src={`${src}?w=800`}
      srcSet={`${src}?w=400 400w, ${src}?w=800 800w, ${src}?w=1200 1200w`}
      sizes="(max-width: 640px) 400px, (max-width: 1024px) 800px, 1200px"
      alt={alt}
      loading="lazy"
      decoding="async"
    />
  );
}

// Convert to WebP/AVIF with sharp
import sharp from 'sharp';

async function optimizeImage(inputPath: string, outputDir: string): Promise<void> {
  const base = path.basename(inputPath, path.extname(inputPath));

  await sharp(inputPath)
    .resize(1200, null, { withoutEnlargement: true })
    .webp({ quality: 80 })
    .toFile(`${outputDir}/${base}.webp`);

  await sharp(inputPath)
    .avif({ quality: 65 })
    .toFile(`${outputDir}/${base}.avif`);
}

CLS Prevention

/* Reserve space for images before they load */
img {
  aspect-ratio: 16/9;
  width: 100%;
  height: auto;
}

/* Reserve space for dynamic content */
.ad-container {
  min-height: 250px; /* Expected ad height */
}

/* Avoid layout shifts from fonts */
@font-face {
  font-family: 'Inter';
  font-display: swap; /* Use fallback font while loading */
}

JavaScript Performance

// Defer non-critical JavaScript
// Bad: Blocks rendering
<script src="/analytics.js"></script>

// Good: Load after page is interactive
<script src="/analytics.js" defer></script>
// or async for independent scripts
<script src="/analytics.js" async></script>

// Measure INP
const observer = new PerformanceObserver((list) => {
  for (const entry of list.getEntries()) {
    if (entry.entryType === 'event') {
      const inp = entry.duration;
      if (inp > 200) {
        console.warn(`Slow interaction: ${entry.name} took ${inp}ms`);
        // Report to analytics
      }
    }
  }
});
observer.observe({ type: 'event', buffered: true });

Performance Monitoring

// Report Web Vitals to analytics
import { onCLS, onINP, onLCP, onFCP, onTTFB } from 'web-vitals';

function sendToAnalytics({ name, value, id }: Metric) {
  navigator.sendBeacon('/analytics', JSON.stringify({
    metric: name,
    value: Math.round(name === 'CLS' ? value * 1000 : value),
    id,
    url: window.location.href,
    userAgent: navigator.userAgent,
  }));
}

onLCP(sendToAnalytics);
onCLS(sendToAnalytics);
onINP(sendToAnalytics);
onFCP(sendToAnalytics);
onTTFB(sendToAnalytics);

Resource Hints

<!-- Preconnect to critical third parties -->
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://api.myapp.com">

<!-- DNS prefetch for non-critical -->
<link rel="dns-prefetch" href="https://cdn.example.com">

<!-- Prefetch next page resources -->
<link rel="prefetch" href="/next-page.js">

Measure first with Lighthouse or WebPageTest, then optimize the biggest wins.