前端性能指标详解与优化实战

性能优化做了不少,但以前只知道“要快”,不理解具体指标。系统学习后分享。

核心指标 (Core Web Vitals)

Google 定义了三个核心指标:

指标 全称 说明 好的标准
LCP Largest Contentful Paint 最大内容渲染时间 < 2.5s
FID First Input Delay 首次输入延迟 < 100ms
CLS Cumulative Layout Shift 累积布局偏移 < 0.1

2024 年新增 INP 替代 FID:

指标 说明 好的标准
INP Interaction to Next Paint 交互到下次绘制

指标详解

LCP (最大内容渲染时间)

页面主要内容加载完成的时间。通常是:

  • 图片
  • 视频封面
  • 文字块

测量:

new PerformanceObserver((entryList) => {
  const entries = entryList.getEntries();
  const lastEntry = entries[entries.length - 1];
  console.log('LCP:', lastEntry.startTime);
}).observe({ type: 'largest-contentful-paint', buffered: true });

INP (交互到下次绘制)

用户交互后到界面更新的时间。

测量:

new PerformanceObserver((list) => {
  for (const entry of list.getEntries()) {
    if (entry.interactionId) {
      console.log('INP:', entry.duration);
    }
  }
}).observe({ type: 'event', buffered: true });

CLS (累积布局偏移)

页面元素位置变化导致的视觉偏移。

测量:

let clsValue = 0;

new PerformanceObserver((entryList) => {
  for (const entry of entryList.getEntries()) {
    if (!entry.hadRecentInput) {
      clsValue += entry.value;
    }
  }
  console.log('CLS:', clsValue);
}).observe({ type: 'layout-shift', buffered: true });

其他重要指标

指标 说明 好的标准
TTFB 首字节时间 < 800ms
FCP 首次内容绘制 < 1.8s
TTI 可交互时间 < 3.8s
TBT 总阻塞时间 < 200ms

性能指标时间线

优化策略

LCP 优化

问题 解决方案
服务端响应慢 CDN、缓存、优化后端
资源加载慢 预加载、压缩、HTTP/2
渲染阻塞 异步加载 JS、内联关键 CSS
<!-- 预加载关键资源 -->
<link rel="preload" href="hero-image.jpg" as="image">

<!-- 预连接第三方域名 -->
<link rel="preconnect" href="https://fonts.googleapis.com">

INP 优化

问题 解决方案
长任务阻塞 拆分任务、Web Worker
事件处理慢 防抖节流、优化逻辑
重渲染过多 React memo、虚拟列表
// 拆分长任务
function processLargeArray(array) {
  const chunk = 100;
  let index = 0;

  function processChunk() {
    const end = Math.min(index + chunk, array.length);
    while (index < end) {
      // 处理 array[index]
      index++;
    }
    if (index < array.length) {
      requestIdleCallback(processChunk);
    }
  }

  processChunk();
}

CLS 优化

问题 解决方案
图片无尺寸 设置 width/height
动态内容插入 预留空间
字体加载闪烁 font-display: swap
<!-- 设置图片尺寸 -->
<img src="hero.jpg" width="800" height="600" alt="">

<!-- 预留广告位空间 -->
<div class="ad-slot" style="min-height: 250px;">
  <!-- 广告加载后填充 -->
</div>
/* 字体加载策略 */
@font-face {
  font-family: 'MyFont';
  src: url('myfont.woff2') format('woff2');
  font-display: swap;
}

监控方案

Web Vitals 库

npm install web-vitals
import { onLCP, onINP, onCLS } from 'web-vitals';

onLCP(console.log);
onINP(console.log);
onCLS(console.log);

上报数据

function sendToAnalytics(metric) {
  const body = JSON.stringify({
    name: metric.name,
    value: metric.value,
    id: metric.id,
    page: window.location.pathname,
  });

  navigator.sendBeacon('/analytics', body);
}

onLCP(sendToAnalytics);
onINP(sendToAnalytics);
onCLS(sendToAnalytics);

建立基线

指标 P50 P75 P95
LCP 1.5s 2.2s 3.5s
INP 80ms 120ms 250ms
CLS 0.02 0.08 0.15

用分位数看分布,不只是平均值。

工具推荐

工具 用途
Lighthouse 本地测试、综合评分
PageSpeed Insights 线上测试、优化建议
Chrome DevTools 性能分析、火焰图
WebPageTest 多地点、多设备测试
Sentry Performance 真实用户监控

Lighthouse 报告

优化案例

案例:LCP 从 4s 到 1.8s

问题分析:

  1. 首页大图 2MB,加载慢
  2. 第三方字体阻塞渲染
  3. 关键 CSS 在外部文件

优化措施:

措施 效果
图片转 WebP + 压缩 -1.5s
字体预加载 -0.4s
关键 CSS 内联 -0.3s

最终 LCP:1.8s

案例:INP 从 300ms 到 80ms

问题分析:

  1. 列表滚动事件处理复杂
  2. 大量 DOM 操作

优化措施:

// 使用 passive 监听器
element.addEventListener('scroll', handler, { passive: true });

// 使用 requestAnimationFrame
let ticking = false;
element.addEventListener('scroll', () => {
  if (!ticking) {
    requestAnimationFrame(() => {
      handler();
      ticking = false;
    });
    ticking = true;
  }
});

总结

性能优化是持续过程,关键是:

  1. 建立监控:知道现状
  2. 设定目标:知道要改善多少
  3. 逐个击破:先解决最大的瓶颈
  4. 持续跟进:防止退化

不要盲目优化,用数据说话。