前端性能指标详解
前端性能指标是衡量网站用户体验的重要标准,直接影响用户满意度、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 }],
},
},
},
};
总结
前端性能指标是优化用户体验的重要指南。通过持续监控和优化这些指标,我们可以:
- 提升用户体验:减少加载时间和交互延迟
- 改善 SEO 排名:Core Web Vitals 是搜索排名因素
- 增加转化率:更快的网站通常有更高的转化率
- 降低运营成本:优化的网站消耗更少的带宽和服务器资源
关键要点:
- 专注于 Core Web Vitals(LCP、FID、CLS)
- 使用 Performance API 进行实时监控
- 实施渐进式优化策略
- 建立性能预算和持续集成
- 结合合成监控和真实用户监控
记住,性能优化是一个持续的过程,需要定期监控、测试和改进。