
Web 性能:核心网页指标优化
理解核心网页指标
LCP (Largest Contentful Paint) - 加载性能
良好: < 2.5s, 需要改进: 2.5-4s, 较差: > 4s
CLS (Cumulative Layout Shift) - 视觉稳定性
良好: < 0.1, 需要改进: 0.1-0.25, 较差: > 0.25
INP (Interaction to Next Paint) - 响应性(取代 FID)
良好: < 200ms, 需要改进: 200-500ms, 较差: > 500ms

LCP 优化
<!-- 预加载 LCP 图片 -->
<link rel="preload" as="image" href="/hero.webp" fetchpriority="high">
<!-- 使用现代图片格式 -->
<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: 为 LCP 图片设置优先级
import Image from 'next/image';
<Image
src="/hero.jpg"
alt="Hero"
width={1200}
height={630}
priority // 预加载,相当于 fetchpriority="high"
sizes="100vw"
/>
图片优化
// 使用 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"
/>
);
}
// 使用 sharp 转换为 WebP/AVIF
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 预防
/* 在图片加载前预留空间 */
img {
aspect-ratio: 16/9;
width: 100%;
height: auto;
}
/* 为动态内容预留空间 */
.ad-container {
min-height: 250px; /* 预期广告高度 */
}
/* 避免字体引起的布局偏移 */
@font-face {
font-family: 'Inter';
font-display: swap; /* 加载时使用后备字体 */
}
JavaScript 性能
// 延迟非关键 JavaScript
// 不好:阻塞渲染
<script src="/analytics.js"></script>
// 好:页面可交互后加载
<script src="/analytics.js" defer></script>
// 或对独立脚本使用 async
<script src="/analytics.js" async></script>
// 测量 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(`慢交互: ${entry.name} 耗时 ${inp}ms`);
// 上报到分析系统
}
}
}
});
observer.observe({ type: 'event', buffered: true });

性能监控
// 将 Web Vitals 上报到分析系统
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);
资源提示
<!-- 预连接到关键的第三方 -->
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://api.myapp.com">
<!-- 对非关键资源进行 DNS 预取 -->
<link rel="dns-prefetch" href="https://cdn.example.com">
<!-- 预取下一页资源 -->
<link rel="prefetch" href="/next-page.js">
首先使用 Lighthouse 或 WebPageTest 进行测量,然后优化最大的收益点。