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

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

核心指标 (Core Web Vitals)

Google 定义了三个核心指标:

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

2024 年新增 INP 替代 FID:

指标说明好的标准
INPInteraction 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);

建立基线

指标P50P75P95
LCP1.5s2.2s3.5s
INP80ms120ms250ms
CLS0.020.080.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. 持续跟进:防止退化

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