Skip to content

前端性能优化清单

前端性能优化是提升用户体验的关键因素。本文提供了一份全面的性能优化清单,帮助开发者系统性地改善应用性能。

🎯 性能指标

Core Web Vitals

Google 的核心网页指标,直接影响 SEO 排名:

  • LCP (Largest Contentful Paint): 最大内容绘制 < 2.5s
  • FID (First Input Delay): 首次输入延迟 < 100ms
  • CLS (Cumulative Layout Shift): 累积布局偏移 < 0.1

其他重要指标

  • FCP (First Contentful Paint): 首次内容绘制 < 1.8s
  • TTI (Time to Interactive): 可交互时间 < 3.8s
  • FMP (First Meaningful Paint): 首次有意义绘制 < 2s
  • Speed Index: 速度指数 < 3.4s

📦 资源优化

✅ 图片优化

格式选择

  • [x] 使用现代图片格式(WebP、AVIF)
  • [x] 为不同设备提供不同尺寸的图片
  • [x] 使用 SVG 替代简单图标
  • [x] 压缩图片文件大小
html
<!-- 响应式图片 -->
<picture>
  <source srcset="image.avif" type="image/avif">
  <source srcset="image.webp" type="image/webp">
  <img src="image.jpg" alt="描述" loading="lazy">
</picture>

<!-- 不同尺寸图片 -->
<img 
  srcset="small.jpg 480w, medium.jpg 800w, large.jpg 1200w"
  sizes="(max-width: 480px) 480px, (max-width: 800px) 800px, 1200px"
  src="medium.jpg" 
  alt="描述"
>

懒加载实现

javascript
// 原生懒加载
<img src="image.jpg" loading="lazy" alt="描述">

// 自定义懒加载
const imageObserver = new IntersectionObserver((entries) => {
  entries.forEach(entry => {
    if (entry.isIntersecting) {
      const img = entry.target;
      img.src = img.dataset.src;
      img.classList.remove('lazy');
      imageObserver.unobserve(img);
    }
  });
});

document.querySelectorAll('img[data-src]').forEach(img => {
  imageObserver.observe(img);
});

✅ 字体优化

字体加载策略

  • [x] 使用 font-display: swap 避免文本闪烁
  • [x] 预加载关键字体文件
  • [x] 使用字体子集减少文件大小
  • [x] 考虑使用系统字体
css
/* 字体优化 */
@font-face {
  font-family: 'CustomFont';
  src: url('font.woff2') format('woff2');
  font-display: swap; /* 立即显示备用字体 */
  unicode-range: U+0000-00FF; /* 字体子集 */
}

/* 系统字体栈 */
body {
  font-family: 
    -apple-system, 
    BlinkMacSystemFont, 
    'Segoe UI', 
    Roboto, 
    'Helvetica Neue', 
    Arial, 
    sans-serif;
}

字体预加载

html
<link rel="preload" href="font.woff2" as="font" type="font/woff2" crossorigin>

✅ CSS 优化

CSS 加载优化

  • [x] 内联关键 CSS
  • [x] 异步加载非关键 CSS
  • [x] 移除未使用的 CSS
  • [x] 使用 CSS 压缩
html
<!-- 关键 CSS 内联 -->
<style>
  /* 首屏关键样式 */
  .header { display: flex; }
  .main { min-height: 100vh; }
</style>

<!-- 非关键 CSS 异步加载 -->
<link rel="preload" href="styles.css" as="style" onload="this.onload=null;this.rel='stylesheet'">
<noscript><link rel="stylesheet" href="styles.css"></noscript>

CSS 性能最佳实践

css
/* 避免复杂选择器 */
/* ❌ 性能差 */
.container .sidebar .widget .title span.highlight {
  color: red;
}

/* ✅ 性能好 */
.widget-title-highlight {
  color: red;
}

/* 使用 transform 和 opacity 进行动画 */
.element {
  transition: transform 0.3s ease, opacity 0.3s ease;
}

.element:hover {
  transform: translateY(-2px);
  opacity: 0.8;
}

/* 避免触发重排的属性 */
/* ❌ 触发重排 */
.element {
  animation: slideIn 0.3s ease;
}

@keyframes slideIn {
  from { left: -100px; }
  to { left: 0; }
}

/* ✅ 只触发重绘 */
.element {
  animation: slideIn 0.3s ease;
}

@keyframes slideIn {
  from { transform: translateX(-100px); }
  to { transform: translateX(0); }
}

✅ JavaScript 优化

代码分割

  • [x] 路由级代码分割
  • [x] 组件级代码分割
  • [x] 第三方库分离
  • [x] 动态导入
javascript
// 路由级代码分割
const HomePage = lazy(() => import('./pages/HomePage'));
const AboutPage = lazy(() => import('./pages/AboutPage'));

// 组件级代码分割
const HeavyComponent = lazy(() => import('./components/HeavyComponent'));

// 条件加载
const loadChart = async () => {
  if (needsChart) {
    const { Chart } = await import('chart.js');
    return Chart;
  }
};

// 第三方库按需加载
import { debounce } from 'lodash-es'; // 只导入需要的函数

Bundle 优化

javascript
// webpack.config.js
module.exports = {
  optimization: {
    splitChunks: {
      chunks: 'all',
      cacheGroups: {
        vendor: {
          test: /[\\/]node_modules[\\/]/,
          name: 'vendors',
          chunks: 'all',
        },
        common: {
          name: 'common',
          minChunks: 2,
          chunks: 'all',
          enforce: true
        }
      }
    }
  }
};

🚀 加载性能优化

✅ 资源预加载

预加载策略

  • [x] DNS 预解析
  • [x] 预连接重要域名
  • [x] 预加载关键资源
  • [x] 预获取下一页资源
html
<!-- DNS 预解析 -->
<link rel="dns-prefetch" href="//api.example.com">
<link rel="dns-prefetch" href="//cdn.example.com">

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

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

<!-- 预获取下一页资源 -->
<link rel="prefetch" href="next-page.html">
<link rel="prefetch" href="next-page.js">

✅ 缓存策略

HTTP 缓存

javascript
// Service Worker 缓存策略
const CACHE_NAME = 'app-v1.0.0';
const urlsToCache = [
  '/',
  '/static/css/main.css',
  '/static/js/main.js'
];

// 缓存优先策略
self.addEventListener('fetch', event => {
  event.respondWith(
    caches.match(event.request)
      .then(response => {
        // 缓存命中,返回缓存
        if (response) {
          return response;
        }
        
        // 网络请求
        return fetch(event.request);
      })
  );
});

// 网络优先策略(适用于 API 请求)
const networkFirst = async (request) => {
  try {
    const response = await fetch(request);
    const cache = await caches.open(CACHE_NAME);
    cache.put(request, response.clone());
    return response;
  } catch (error) {
    const cachedResponse = await caches.match(request);
    return cachedResponse || new Response('Offline');
  }
};

浏览器缓存配置

nginx
# Nginx 缓存配置
location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg)$ {
  expires 1y;
  add_header Cache-Control "public, immutable";
}

location ~* \.(html)$ {
  expires 1h;
  add_header Cache-Control "public, must-revalidate";
}

✅ 压缩优化

Gzip/Brotli 压缩

nginx
# Nginx 压缩配置
gzip on;
gzip_vary on;
gzip_min_length 1024;
gzip_types
  text/plain
  text/css
  text/xml
  text/javascript
  application/javascript
  application/xml+rss
  application/json;

# Brotli 压缩(更好的压缩率)
brotli on;
brotli_comp_level 6;
brotli_types
  text/plain
  text/css
  application/json
  application/javascript
  text/xml
  application/xml
  application/xml+rss
  text/javascript;

⚡ 运行时性能优化

✅ JavaScript 性能

避免阻塞主线程

javascript
// 使用 Web Workers 处理重计算
// main.js
const worker = new Worker('worker.js');
worker.postMessage({ data: largeDataSet });
worker.onmessage = (e) => {
  console.log('计算结果:', e.data);
};

// worker.js
self.onmessage = (e) => {
  const result = heavyComputation(e.data);
  self.postMessage(result);
};

// 使用 requestIdleCallback 延迟非关键任务
const performNonCriticalWork = (deadline) => {
  while (deadline.timeRemaining() > 0 && tasks.length > 0) {
    const task = tasks.shift();
    task();
  }
  
  if (tasks.length > 0) {
    requestIdleCallback(performNonCriticalWork);
  }
};

requestIdleCallback(performNonCriticalWork);

// 时间分片处理大量数据
const processLargeList = (list, chunkSize = 1000) => {
  let index = 0;
  
  const processChunk = () => {
    const chunk = list.slice(index, index + chunkSize);
    
    chunk.forEach(item => {
      // 处理单个项目
      processItem(item);
    });
    
    index += chunkSize;
    
    if (index < list.length) {
      setTimeout(processChunk, 0); // 让出控制权
    }
  };
  
  processChunk();
};

内存管理

javascript
// 避免内存泄漏
class ComponentManager {
  constructor() {
    this.listeners = [];
    this.timers = [];
    this.observers = [];
  }
  
  addListener(element, event, handler) {
    element.addEventListener(event, handler);
    this.listeners.push({ element, event, handler });
  }
  
  addTimer(callback, delay) {
    const timerId = setTimeout(callback, delay);
    this.timers.push(timerId);
    return timerId;
  }
  
  addObserver(observer) {
    this.observers.push(observer);
  }
  
  cleanup() {
    // 清理事件监听器
    this.listeners.forEach(({ element, event, handler }) => {
      element.removeEventListener(event, handler);
    });
    
    // 清理定时器
    this.timers.forEach(timerId => {
      clearTimeout(timerId);
    });
    
    // 清理观察者
    this.observers.forEach(observer => {
      observer.disconnect();
    });
    
    // 清空数组
    this.listeners = [];
    this.timers = [];
    this.observers = [];
  }
}

// 对象池模式减少 GC 压力
class ObjectPool {
  constructor(createFn, resetFn, initialSize = 10) {
    this.createFn = createFn;
    this.resetFn = resetFn;
    this.pool = [];
    
    // 预创建对象
    for (let i = 0; i < initialSize; i++) {
      this.pool.push(this.createFn());
    }
  }
  
  acquire() {
    return this.pool.length > 0 ? this.pool.pop() : this.createFn();
  }
  
  release(obj) {
    this.resetFn(obj);
    this.pool.push(obj);
  }
}

✅ DOM 操作优化

批量 DOM 操作

javascript
// ❌ 多次 DOM 操作
items.forEach(item => {
  const element = document.createElement('div');
  element.textContent = item.text;
  container.appendChild(element);
});

// ✅ 批量 DOM 操作
const fragment = document.createDocumentFragment();
items.forEach(item => {
  const element = document.createElement('div');
  element.textContent = item.text;
  fragment.appendChild(element);
});
container.appendChild(fragment);

// 使用 innerHTML 批量创建
const html = items.map(item => `<div>${item.text}</div>`).join('');
container.innerHTML = html;

虚拟滚动

javascript
// 虚拟滚动实现
class VirtualScroller {
  constructor(container, items, itemHeight, renderItem) {
    this.container = container;
    this.items = items;
    this.itemHeight = itemHeight;
    this.renderItem = renderItem;
    
    this.visibleStart = 0;
    this.visibleEnd = 0;
    this.scrollTop = 0;
    
    this.init();
  }
  
  init() {
    this.container.style.height = `${this.items.length * this.itemHeight}px`;
    this.container.style.position = 'relative';
    
    this.container.addEventListener('scroll', this.handleScroll.bind(this));
    this.updateVisibleItems();
  }
  
  handleScroll() {
    this.scrollTop = this.container.scrollTop;
    this.updateVisibleItems();
  }
  
  updateVisibleItems() {
    const containerHeight = this.container.clientHeight;
    
    this.visibleStart = Math.floor(this.scrollTop / this.itemHeight);
    this.visibleEnd = Math.min(
      this.visibleStart + Math.ceil(containerHeight / this.itemHeight) + 1,
      this.items.length
    );
    
    this.render();
  }
  
  render() {
    const visibleItems = this.items.slice(this.visibleStart, this.visibleEnd);
    
    this.container.innerHTML = '';
    
    visibleItems.forEach((item, index) => {
      const element = this.renderItem(item);
      element.style.position = 'absolute';
      element.style.top = `${(this.visibleStart + index) * this.itemHeight}px`;
      element.style.height = `${this.itemHeight}px`;
      
      this.container.appendChild(element);
    });
  }
}

🎨 渲染性能优化

✅ CSS 性能

避免重排和重绘

css
/* 使用 transform 替代改变位置 */
.element {
  /* ❌ 触发重排 */
  /* left: 100px; */
  
  /* ✅ 只触发合成 */
  transform: translateX(100px);
}

/* 使用 opacity 替代 visibility */
.element {
  /* ❌ 触发重绘 */
  /* visibility: hidden; */
  
  /* ✅ 只触发合成 */
  opacity: 0;
}

/* 启用硬件加速 */
.accelerated {
  transform: translateZ(0); /* 或 will-change: transform; */
}

优化 CSS 选择器

css
/* ❌ 复杂选择器 */
.container .sidebar .widget .title span.highlight {
  color: red;
}

/* ✅ 简单选择器 */
.widget-title-highlight {
  color: red;
}

/* ❌ 通用选择器 */
* {
  box-sizing: border-box;
}

/* ✅ 具体选择器 */
html {
  box-sizing: border-box;
}

*, *:before, *:after {
  box-sizing: inherit;
}

✅ 动画优化

高性能动画

css
/* 只对 transform 和 opacity 做动画 */
.smooth-animation {
  transition: transform 0.3s ease, opacity 0.3s ease;
  will-change: transform, opacity;
}

.smooth-animation:hover {
  transform: scale(1.05) translateY(-2px);
  opacity: 0.9;
}

/* 使用 CSS 动画替代 JavaScript */
@keyframes fadeInUp {
  from {
    opacity: 0;
    transform: translateY(20px);
  }
  to {
    opacity: 1;
    transform: translateY(0);
  }
}

.fade-in-up {
  animation: fadeInUp 0.5s ease-out;
}

JavaScript 动画优化

javascript
// 使用 requestAnimationFrame
const animateElement = (element, start, end, duration) => {
  const startTime = performance.now();
  
  const animate = (currentTime) => {
    const elapsed = currentTime - startTime;
    const progress = Math.min(elapsed / duration, 1);
    
    const current = start + (end - start) * easeOutCubic(progress);
    element.style.transform = `translateX(${current}px)`;
    
    if (progress < 1) {
      requestAnimationFrame(animate);
    }
  };
  
  requestAnimationFrame(animate);
};

// 缓动函数
const easeOutCubic = (t) => 1 - Math.pow(1 - t, 3);

// 使用 Web Animations API
element.animate([
  { transform: 'translateX(0px)', opacity: 1 },
  { transform: 'translateX(100px)', opacity: 0 }
], {
  duration: 1000,
  easing: 'ease-out'
});

📱 移动端优化

✅ 触摸优化

触摸响应优化

css
/* 移除点击延迟 */
.button {
  touch-action: manipulation;
}

/* 优化滚动性能 */
.scroll-container {
  -webkit-overflow-scrolling: touch;
  overflow-scrolling: touch;
}

/* 防止意外缩放 */
.no-zoom {
  touch-action: pan-x pan-y;
}

视口优化

html
<!-- 基础视口设置 -->
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">

<!-- 防止缩放 -->
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no">

✅ 网络优化

离线支持

javascript
// Service Worker 离线策略
const CACHE_NAME = 'offline-cache-v1';

self.addEventListener('fetch', event => {
  if (event.request.destination === 'document') {
    event.respondWith(
      fetch(event.request)
        .catch(() => caches.match('/offline.html'))
    );
  }
});

// 网络状态检测
const handleNetworkChange = () => {
  if (navigator.onLine) {
    // 网络恢复,同步离线数据
    syncOfflineData();
  } else {
    // 网络断开,启用离线模式
    enableOfflineMode();
  }
};

window.addEventListener('online', handleNetworkChange);
window.addEventListener('offline', handleNetworkChange);

🔍 性能监控

✅ 性能指标收集

Core Web Vitals 监控

javascript
// LCP 监控
new PerformanceObserver((entryList) => {
  for (const entry of entryList.getEntries()) {
    console.log('LCP:', entry.startTime);
    // 发送到分析服务
    analytics.track('lcp', entry.startTime);
  }
}).observe({ entryTypes: ['largest-contentful-paint'] });

// FID 监控
new PerformanceObserver((entryList) => {
  for (const entry of entryList.getEntries()) {
    console.log('FID:', entry.processingStart - entry.startTime);
    analytics.track('fid', entry.processingStart - entry.startTime);
  }
}).observe({ entryTypes: ['first-input'] });

// CLS 监控
let clsValue = 0;
new PerformanceObserver((entryList) => {
  for (const entry of entryList.getEntries()) {
    if (!entry.hadRecentInput) {
      clsValue += entry.value;
    }
  }
  console.log('CLS:', clsValue);
  analytics.track('cls', clsValue);
}).observe({ entryTypes: ['layout-shift'] });

自定义性能监控

javascript
class PerformanceMonitor {
  constructor() {
    this.metrics = {};
    this.observers = [];
  }
  
  // 标记开始时间
  mark(name) {
    performance.mark(`${name}-start`);
  }
  
  // 测量耗时
  measure(name) {
    performance.mark(`${name}-end`);
    performance.measure(name, `${name}-start`, `${name}-end`);
    
    const measure = performance.getEntriesByName(name, 'measure')[0];
    this.metrics[name] = measure.duration;
    
    return measure.duration;
  }
  
  // 监控资源加载
  observeResources() {
    const observer = new PerformanceObserver((list) => {
      for (const entry of list.getEntries()) {
        if (entry.duration > 1000) {
          console.warn('Slow resource:', entry.name, entry.duration);
        }
      }
    });
    
    observer.observe({ entryTypes: ['resource'] });
    this.observers.push(observer);
  }
  
  // 监控长任务
  observeLongTasks() {
    const observer = new PerformanceObserver((list) => {
      for (const entry of list.getEntries()) {
        console.warn('Long task detected:', entry.duration);
        analytics.track('long-task', {
          duration: entry.duration,
          startTime: entry.startTime
        });
      }
    });
    
    observer.observe({ entryTypes: ['longtask'] });
    this.observers.push(observer);
  }
  
  // 获取性能报告
  getReport() {
    const navigation = performance.getEntriesByType('navigation')[0];
    
    return {
      // 页面加载时间
      loadTime: navigation.loadEventEnd - navigation.fetchStart,
      // DOM 解析时间
      domParseTime: navigation.domContentLoadedEventEnd - navigation.domLoading,
      // 资源加载时间
      resourceLoadTime: navigation.loadEventEnd - navigation.domContentLoadedEventEnd,
      // 自定义指标
      customMetrics: this.metrics
    };
  }
}

const monitor = new PerformanceMonitor();
monitor.observeResources();
monitor.observeLongTasks();

🛠️ 性能工具

✅ 开发工具

Chrome DevTools

  • Performance 面板分析运行时性能
  • Network 面板优化资源加载
  • Lighthouse 综合性能评估
  • Coverage 面板找出未使用代码

性能分析工具

javascript
// 性能分析装饰器
const performanceProfile = (target, propertyKey, descriptor) => {
  const originalMethod = descriptor.value;
  
  descriptor.value = function(...args) {
    const start = performance.now();
    const result = originalMethod.apply(this, args);
    const end = performance.now();
    
    console.log(`${propertyKey} 执行时间: ${end - start}ms`);
    
    return result;
  };
  
  return descriptor;
};

// 使用示例
class DataProcessor {
  @performanceProfile
  processLargeDataset(data) {
    // 处理大量数据
    return data.map(item => transform(item));
  }
}

✅ 构建优化

Webpack 优化

javascript
// webpack.config.js
const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin;

module.exports = {
  optimization: {
    // 代码分割
    splitChunks: {
      chunks: 'all',
      cacheGroups: {
        vendor: {
          test: /[\\/]node_modules[\\/]/,
          name: 'vendors',
          chunks: 'all',
        }
      }
    },
    
    // 压缩
    minimize: true,
    minimizer: [
      new TerserPlugin({
        terserOptions: {
          compress: {
            drop_console: true, // 移除 console
            drop_debugger: true // 移除 debugger
          }
        }
      })
    ]
  },
  
  plugins: [
    // 分析包大小
    new BundleAnalyzerPlugin({
      analyzerMode: 'static',
      openAnalyzer: false
    })
  ]
};

📋 性能检查清单

🎯 加载性能

  • [ ] 首屏时间 < 2.5s
  • [ ] 资源压缩(Gzip/Brotli)
  • [ ] 图片优化(格式、尺寸、懒加载)
  • [ ] 字体优化(预加载、font-display)
  • [ ] CSS 优化(关键路径、异步加载)
  • [ ] JavaScript 优化(代码分割、Tree Shaking)
  • [ ] 缓存策略(HTTP 缓存、Service Worker)

⚡ 运行时性能

  • [ ] 避免长任务(> 50ms)
  • [ ] 内存泄漏检查
  • [ ] DOM 操作优化
  • [ ] 事件处理优化
  • [ ] 动画性能优化
  • [ ] 虚拟滚动(长列表)

📱 移动端优化

  • [ ] 触摸响应优化
  • [ ] 视口配置
  • [ ] 网络适配
  • [ ] 离线支持
  • [ ] 电池优化

🔍 监控与分析

  • [ ] Core Web Vitals 监控
  • [ ] 错误监控
  • [ ] 性能指标收集
  • [ ] 用户体验监控
  • [ ] A/B 测试

总结

前端性能优化是一个持续的过程,需要:

  1. 建立性能文化:将性能作为开发的重要考量
  2. 持续监控:建立完善的性能监控体系
  3. 数据驱动:基于真实数据进行优化决策
  4. 用户体验优先:始终以用户体验为核心目标
  5. 渐进式优化:从影响最大的问题开始优化

通过系统性地应用这些优化策略,可以显著提升应用的性能表现和用户体验。

参考资源

vitepress开发指南