Skip to content

前端性能指标详解

前端性能指标是衡量网站用户体验的重要标准,直接影响用户满意度、SEO 排名和业务转化率。本文将详细介绍各种前端性能指标,帮助开发者理解和优化网站性能

目录

Core Web Vitals 核心指标

Google 在 2020 年推出的 Core Web Vitals 是衡量网站用户体验的三个核心指标,也是 SEO 排名的重要因素。

1. LCP (Largest Contentful Paint) 最大内容绘制

定义:页面开始加载到最大文本块或图像元素在屏幕上完成渲染的时间。

评判标准

  • 良好:≤ 2.5 秒
  • 需要改进:2.5 - 4.0 秒
  • 差:> 4.0 秒

影响 LCP 的因素

  • 服务器响应时间
  • 阻塞渲染的 CSS 和 JavaScript
  • 资源加载时间
  • 客户端渲染

优化策略

javascript
// 预加载关键资源
<link rel="preload" href="hero-image.jpg" as="image">

// 优化图片加载
<img src="hero-image.jpg" loading="eager" fetchpriority="high">

// 使用 CDN 加速
<link rel="preconnect" href="https://cdn.example.com">

2. FID (First Input Delay) 首次输入延迟

定义:用户首次与页面交互(点击、触摸、按键)到浏览器实际开始处理事件处理程序的时间。

评判标准

  • 良好:≤ 100 毫秒
  • 需要改进:100 - 300 毫秒
  • 差:> 300 毫秒

影响 FID 的因素

  • 主线程阻塞
  • JavaScript 执行时间过长
  • 大型第三方脚本

优化策略

javascript
// 代码分割,按需加载
import { heavyFunction } from './heavy-module.js';

// 使用 Web Workers 处理复杂计算
const worker = new Worker('calculation-worker.js');
worker.postMessage(data);

// 延迟非关键 JavaScript
<script defer src="non-critical.js"></script>;

3. CLS (Cumulative Layout Shift) 累积布局偏移

定义:测量页面生命周期内所有意外布局偏移的累积分数。

评判标准

  • 良好:≤ 0.1
  • 需要改进:0.1 - 0.25
  • 差:> 0.25

影响 CLS 的因素

  • 没有尺寸的图像
  • 动态注入的内容
  • Web 字体加载
  • 广告和第三方小部件

优化策略

css
/* 为图像设置固定尺寸 */
.image-container {
  width: 400px;
  height: 300px;
  aspect-ratio: 4/3;
}

/* 使用 font-display 优化字体加载 */
@font-face {
  font-family: 'CustomFont';
  src: url('font.woff2') format('woff2');
  font-display: swap;
}

/* 为动态内容预留空间 */
.ad-placeholder {
  min-height: 250px;
  width: 300px;
}

传统性能指标

1. FP (First Paint) 首次绘制

定义:浏览器首次向屏幕绘制像素的时间点。

javascript
// 测量 FP
const observer = new PerformanceObserver((list) => {
  for (const entry of list.getEntries()) {
    if (entry.name === 'first-paint') {
      console.log('FP:', entry.startTime);
    }
  }
});
observer.observe({ entryTypes: ['paint'] });

2. FCP (First Contentful Paint) 首次内容绘制

定义:浏览器首次绘制来自 DOM 的内容的时间。

评判标准

  • 良好:≤ 1.8 秒
  • 需要改进:1.8 - 3.0 秒
  • 差:> 3.0 秒
javascript
// 测量 FCP
const observer = new PerformanceObserver((list) => {
  for (const entry of list.getEntries()) {
    if (entry.name === 'first-contentful-paint') {
      console.log('FCP:', entry.startTime);
    }
  }
});
observer.observe({ entryTypes: ['paint'] });

3. TTI (Time to Interactive) 可交互时间

定义:页面变得完全可交互的时间点。

评判标准

  • 良好:≤ 3.8 秒
  • 需要改进:3.8 - 7.3 秒
  • 差:> 7.3 秒

4. SI (Speed Index) 速度指数

定义:页面内容填充速度的指标,值越低越好。

评判标准

  • 良好:≤ 3.4 秒
  • 需要改进:3.4 - 5.8 秒
  • 差:> 5.8 秒

5. TBT (Total Blocking Time) 总阻塞时间

定义:FCP 到 TTI 之间所有阻塞时间的总和。

评判标准

  • 良好:≤ 200 毫秒
  • 需要改进:200 - 600 毫秒
  • 差:> 600 毫秒

网络性能指标

1. TTFB (Time to First Byte) 首字节时间

定义:从用户发起请求到接收到第一个字节的时间。

javascript
// 测量 TTFB
const navigationTiming = performance.getEntriesByType('navigation')[0];
const ttfb = navigationTiming.responseStart - navigationTiming.requestStart;
console.log('TTFB:', ttfb);

2. DNS 查询时间

javascript
// 测量 DNS 查询时间
const dnsTime = navigationTiming.domainLookupEnd - navigationTiming.domainLookupStart;
console.log('DNS Time:', dnsTime);

3. TCP 连接时间

javascript
// 测量 TCP 连接时间
const tcpTime = navigationTiming.connectEnd - navigationTiming.connectStart;
console.log('TCP Time:', tcpTime);

4. SSL 握手时间

javascript
// 测量 SSL 握手时间(如果是 HTTPS)
if (navigationTiming.secureConnectionStart > 0) {
  const sslTime = navigationTiming.connectEnd - navigationTiming.secureConnectionStart;
  console.log('SSL Time:', sslTime);
}

JavaScript 性能指标

1. 内存使用情况

javascript
// 监控内存使用
function checkMemoryUsage() {
  if ('memory' in performance) {
    const memory = performance.memory;
    console.log({
      usedJSHeapSize: memory.usedJSHeapSize,
      totalJSHeapSize: memory.totalJSHeapSize,
      jsHeapSizeLimit: memory.jsHeapSizeLimit,
    });
  }
}

setInterval(checkMemoryUsage, 5000);

2. 长任务监控

javascript
// 监控长任务(超过 50ms 的任务)
const observer = new PerformanceObserver((list) => {
  for (const entry of list.getEntries()) {
    console.log('Long task detected:', {
      name: entry.name,
      duration: entry.duration,
      startTime: entry.startTime,
    });
  }
});
observer.observe({ entryTypes: ['longtask'] });

3. 事件循环延迟

javascript
// 测量事件循环延迟
function measureEventLoopDelay() {
  const start = performance.now();
  setTimeout(() => {
    const delay = performance.now() - start;
    console.log('Event loop delay:', delay);
  }, 0);
}

setInterval(measureEventLoopDelay, 1000);

性能测量工具

1. Performance API

javascript
// 综合性能监控类
class PerformanceMonitor {
  constructor() {
    this.metrics = {};
    this.initObservers();
  }

  initObservers() {
    // 监控绘制指标
    new PerformanceObserver((list) => {
      for (const entry of list.getEntries()) {
        this.metrics[entry.name] = entry.startTime;
      }
    }).observe({ entryTypes: ['paint'] });

    // 监控导航指标
    new PerformanceObserver((list) => {
      for (const entry of list.getEntries()) {
        this.calculateNavigationMetrics(entry);
      }
    }).observe({ entryTypes: ['navigation'] });

    // 监控资源加载
    new PerformanceObserver((list) => {
      for (const entry of list.getEntries()) {
        this.trackResourceLoading(entry);
      }
    }).observe({ entryTypes: ['resource'] });
  }

  calculateNavigationMetrics(entry) {
    this.metrics.dns = entry.domainLookupEnd - entry.domainLookupStart;
    this.metrics.tcp = entry.connectEnd - entry.connectStart;
    this.metrics.request = entry.responseStart - entry.requestStart;
    this.metrics.response = entry.responseEnd - entry.responseStart;
    this.metrics.domParse = entry.domContentLoadedEventStart - entry.responseEnd;
    this.metrics.domReady = entry.domContentLoadedEventEnd - entry.domContentLoadedEventStart;
    this.metrics.load = entry.loadEventEnd - entry.loadEventStart;
  }

  trackResourceLoading(entry) {
    if (entry.transferSize === 0) {
      console.log('Resource loaded from cache:', entry.name);
    }

    const loadTime = entry.responseEnd - entry.startTime;
    if (loadTime > 1000) {
      console.warn('Slow resource detected:', entry.name, loadTime + 'ms');
    }
  }

  getMetrics() {
    return this.metrics;
  }

  sendMetrics() {
    // 发送性能数据到分析服务
    fetch('/api/performance', {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify(this.metrics),
    });
  }
}

// 使用示例
const monitor = new PerformanceMonitor();
window.addEventListener('load', () => {
  setTimeout(() => {
    console.log('Performance Metrics:', monitor.getMetrics());
    monitor.sendMetrics();
  }, 1000);
});

2. Lighthouse CI 集成

yaml
# .github/workflows/lighthouse.yml
name: Lighthouse CI
on: [push]
jobs:
  lhci:
    name: Lighthouse
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v3
      - name: Use Node.js
        uses: actions/setup-node@v3
        with:
          node-version: '18'
      - name: Install dependencies
        run: npm ci
      - name: Build
        run: npm run build
      - name: Run Lighthouse CI
        run: |
          npm install -g @lhci/cli@0.11.x
          lhci autorun

3. Web Vitals 监控

javascript
// 安装:npm install web-vitals
import { getCLS, getFID, getFCP, getLCP, getTTFB } from 'web-vitals';

function sendToAnalytics(metric) {
  // 发送到 Google Analytics
  gtag('event', metric.name, {
    event_category: 'Web Vitals',
    event_label: metric.id,
    value: Math.round(metric.name === 'CLS' ? metric.value * 1000 : metric.value),
    non_interaction: true,
  });
}

getCLS(sendToAnalytics);
getFID(sendToAnalytics);
getFCP(sendToAnalytics);
getLCP(sendToAnalytics);
getTTFB(sendToAnalytics);

性能优化策略

1. 资源优化

javascript
// 图片懒加载
class LazyImageLoader {
  constructor() {
    this.imageObserver = new IntersectionObserver(this.handleIntersection.bind(this));
    this.init();
  }

  init() {
    const lazyImages = document.querySelectorAll('img[data-lazy]');
    lazyImages.forEach((img) => this.imageObserver.observe(img));
  }

  handleIntersection(entries) {
    entries.forEach((entry) => {
      if (entry.isIntersecting) {
        const img = entry.target;
        img.src = img.dataset.lazy;
        img.removeAttribute('data-lazy');
        this.imageObserver.unobserve(img);
      }
    });
  }
}

new LazyImageLoader();

2. 代码分割与懒加载

javascript
// 路由级代码分割
import { createRouter, createWebHistory } from 'vue-router';

const router = createRouter({
  history: createWebHistory(),
  routes: [
    {
      path: '/',
      component: () => import('./views/Home.vue'),
    },
    {
      path: '/about',
      component: () => import('./views/About.vue'),
    },
  ],
});

// 动态导入
const loadModule = async (moduleName) => {
  const module = await import(`./modules/${moduleName}.js`);
  return module.default;
};

3. 缓存策略

javascript
// Service Worker 缓存策略
self.addEventListener('fetch', (event) => {
  if (event.request.destination === 'image') {
    event.respondWith(
      caches.open('images').then((cache) => {
        return cache.match(event.request).then((response) => {
          if (response) {
            return response;
          }

          return fetch(event.request).then((networkResponse) => {
            cache.put(event.request, networkResponse.clone());
            return networkResponse;
          });
        });
      })
    );
  }
});

4. 预加载策略

html
<!-- DNS 预解析 -->
<link rel="dns-prefetch" href="//example.com" />

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

<!-- 预加载关键资源 -->
<link rel="preload" href="/critical.css" as="style" />
<link rel="preload" href="/hero-image.jpg" as="image" />

<!-- 预获取非关键资源 -->
<link rel="prefetch" href="/next-page.js" />

5. JavaScript 优化

javascript
// 防抖和节流
function debounce(func, delay) {
  let timeoutId;
  return function (...args) {
    clearTimeout(timeoutId);
    timeoutId = setTimeout(() => func.apply(this, args), delay);
  };
}

function throttle(func, limit) {
  let inThrottle;
  return function (...args) {
    if (!inThrottle) {
      func.apply(this, args);
      inThrottle = true;
      setTimeout(() => (inThrottle = false), limit);
    }
  };
}

// 使用示例
const handleScroll = throttle(() => {
  console.log('Scroll event');
}, 100);

const handleResize = debounce(() => {
  console.log('Resize event');
}, 250);

window.addEventListener('scroll', handleScroll);
window.addEventListener('resize', handleResize);

6. CSS 优化

css
/* 使用 contain 属性优化布局 */
.card {
  contain: layout style paint;
}

/* 使用 will-change 提示浏览器优化 */
.animated-element {
  will-change: transform;
}

/* 避免触发布局的 CSS 属性 */
.smooth-animation {
  transform: translateX(100px); /* 好:只触发合成 */
  /* left: 100px; 坏:触发布局 */
}

/* 使用 CSS Grid 和 Flexbox */
.grid-container {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
  gap: 1rem;
}

性能监控最佳实践

1. 真实用户监控 (RUM)

javascript
// RUM 数据收集
class RealUserMonitoring {
  constructor(apiEndpoint) {
    this.apiEndpoint = apiEndpoint;
    this.data = {
      url: window.location.href,
      userAgent: navigator.userAgent,
      timestamp: Date.now(),
      connection: this.getConnectionInfo(),
    };
    this.collectMetrics();
  }

  getConnectionInfo() {
    if ('connection' in navigator) {
      const conn = navigator.connection;
      return {
        effectiveType: conn.effectiveType,
        downlink: conn.downlink,
        rtt: conn.rtt,
      };
    }
    return null;
  }

  collectMetrics() {
    // 收集 Core Web Vitals
    import('web-vitals').then(({ getCLS, getFID, getFCP, getLCP, getTTFB }) => {
      getCLS((metric) => this.addMetric('CLS', metric));
      getFID((metric) => this.addMetric('FID', metric));
      getFCP((metric) => this.addMetric('FCP', metric));
      getLCP((metric) => this.addMetric('LCP', metric));
      getTTFB((metric) => this.addMetric('TTFB', metric));
    });

    // 页面卸载时发送数据
    window.addEventListener('beforeunload', () => {
      this.sendData();
    });
  }

  addMetric(name, metric) {
    this.data[name] = {
      value: metric.value,
      delta: metric.delta,
      id: metric.id,
    };
  }

  sendData() {
    if ('sendBeacon' in navigator) {
      navigator.sendBeacon(this.apiEndpoint, JSON.stringify(this.data));
    } else {
      fetch(this.apiEndpoint, {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify(this.data),
        keepalive: true,
      });
    }
  }
}

// 初始化 RUM
new RealUserMonitoring('/api/performance');

2. 性能预算

javascript
// lighthouse.config.js
module.exports = {
  ci: {
    assert: {
      assertions: {
        'categories:performance': ['warn', { minScore: 0.9 }],
        'first-contentful-paint': ['error', { maxNumericValue: 2000 }],
        'largest-contentful-paint': ['error', { maxNumericValue: 2500 }],
        'cumulative-layout-shift': ['error', { maxNumericValue: 0.1 }],
        'total-blocking-time': ['warn', { maxNumericValue: 300 }],
      },
    },
  },
};

总结

前端性能指标是优化用户体验的重要指南。通过持续监控和优化这些指标,我们可以:

  1. 提升用户体验:减少加载时间和交互延迟
  2. 改善 SEO 排名:Core Web Vitals 是搜索排名因素
  3. 增加转化率:更快的网站通常有更高的转化率
  4. 降低运营成本:优化的网站消耗更少的带宽和服务器资源

关键要点

  • 专注于 Core Web Vitals(LCP、FID、CLS)
  • 使用 Performance API 进行实时监控
  • 实施渐进式优化策略
  • 建立性能预算和持续集成
  • 结合合成监控和真实用户监控

记住,性能优化是一个持续的过程,需要定期监控、测试和改进。

Released under the MIT License.