Web 前端性能优化:从代码到加载速度的全面剖析

Web 前端性能优化:从代码到加载速度的全面剖析

当用户访问你的网站时,如果页面加载时间超过3秒,跳出率会飙升至40%以上。更糟糕的是,移动端用户的耐心只有2秒。这意味着性能优化不仅仅是技术问题,更直接关系到业务成果。

经过多年的前端开发实践,我发现很多开发者在性能优化时存在一个误区:过分关注工具和框架的选择,却忽略了最基础但最关键的优化策略。今天我们就来深入剖析前端性能优化的核心要点。

性能优化的核心指标

在开始优化之前,我们需要明确衡量标准。Google 提出的 Core Web Vitals 为我们提供了科学的评估体系:

关键性能指标

LCP (Largest Contentful Paint)

  • 衡量页面主要内容的加载速度
  • 理想值:< 2.5秒
  • 影响因素:服务器响应时间、资源加载时间、客户端渲染时间

FID (First Input Delay)

  • 衡量页面的交互响应性
  • 理想值:< 100毫秒
  • 影响因素:JavaScript执行时间、主线程阻塞

CLS (Cumulative Layout Shift)

  • 衡量视觉稳定性
  • 理想值:< 0.1
  • 影响因素:图片尺寸、动态内容插入、字体加载
// 性能监控代码示例
class PerformanceMonitor {
  constructor() {
    this.metrics = {};
    this.observer = null;
    this.initObserver();
  }

  initObserver() {
    // 监控 LCP
    this.observer = new PerformanceObserver((list) => {
      const entries = list.getEntries();
      entries.forEach((entry) => {
        if (entry.entryType === 'largest-contentful-paint') {
          this.metrics.lcp = entry.startTime;
          this.reportMetric('LCP', entry.startTime);
        }
      });
    });
    
    this.observer.observe({ entryTypes: ['largest-contentful-paint'] });
    
    // 监控 FID
    this.observeFID();
    
    // 监控 CLS
    this.observeCLS();
  }

  observeFID() {
    new PerformanceObserver((list) => {
      const entries = list.getEntries();
      entries.forEach((entry) => {
        if (entry.entryType === 'first-input') {
          const fid = entry.processingStart - entry.startTime;
          this.metrics.fid = fid;
          this.reportMetric('FID', fid);
        }
      });
    }).observe({ entryTypes: ['first-input'] });
  }

  observeCLS() {
    let clsValue = 0;
    new PerformanceObserver((list) => {
      const entries = list.getEntries();
      entries.forEach((entry) => {
        if (!entry.hadRecentInput) {
          clsValue += entry.value;
        }
      });
      this.metrics.cls = clsValue;
      this.reportMetric('CLS', clsValue);
    }).observe({ entryTypes: ['layout-shift'] });
  }

  reportMetric(name, value) {
    // 发送性能数据到分析平台
    fetch('/api/performance', {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({ metric: name, value, timestamp: Date.now() })
    }).catch(err => console.error('性能数据上报失败:', err));
  }
}

// 初始化性能监控
const monitor = new PerformanceMonitor();

代码层面的性能优化

JavaScript 优化策略

1. 减少主线程阻塞

JavaScript 是单线程的,长时间运行的任务会阻塞用户交互。我们需要将耗时任务拆分:

// 问题代码:大量计算阻塞主线程
function processLargeDataset(data) {
  const result = [];
  for (let i = 0; i < data.length; i++) {
    // 复杂计算
    result.push(complexCalculation(data[i]));
  }
  return result;
}

// 优化方案:时间切片处理
class TaskScheduler {
  constructor() {
    this.tasks = [];
    this.isRunning = false;
  }

  addTask(task) {
    this.tasks.push(task);
    if (!this.isRunning) {
      this.runTasks();
    }
  }

  runTasks() {
    this.isRunning = true;
    const runChunk = () => {
      const start = performance.now();
      
      // 在5ms时间片内执行任务
      while (this.tasks.length > 0 && performance.now() - start < 5) {
        const task = this.tasks.shift();
        task();
      }
      
      if (this.tasks.length > 0) {
        // 让出控制权给浏览器
        requestIdleCallback(runChunk);
      } else {
        this.isRunning = false;
      }
    };
    
    requestIdleCallback(runChunk);
  }
}

// 使用示例
function processLargeDatasetOptimized(data) {
  const result = [];
  const scheduler = new TaskScheduler();
  
  return new Promise((resolve) => {
    let index = 0;
    
    const processChunk = () => {
      if (index < data.length) {
        scheduler.addTask(() => {
          result.push(complexCalculation(data[index]));
          index++;
          processChunk();
        });
      } else {
        resolve(result);
      }
    };
    
    processChunk();
  });
}

2. 内存优化与垃圾回收

内存泄漏是性能杀手,特别是在单页应用中:

// 内存泄漏的常见场景和解决方案
class ComponentManager {
  constructor() {
    this.components = new Map();
    this.observers = new WeakMap(); // 使用 WeakMap 避免内存泄漏
    this.timers = new Set();
  }

  createComponent(id, config) {
    const component = {
      id,
      config,
      cleanup: () => {
        // 清理事件监听器
        this.removeEventListeners(component);
        // 清理定时器
        this.clearTimers(component);
        // 清理 DOM 引用
        this.clearDOMReferences(component);
      }
    };

    this.components.set(id, component);
    return component;
  }

  removeComponent(id) {
    const component = this.components.get(id);
    if (component) {
      // 执行清理函数
      component.cleanup();
      this.components.delete(id);
    }
  }

  // 安全的定时器管理
  setTimeout(callback, delay, componentId) {
    const timerId = setTimeout(() => {
      callback();
      this.timers.delete(timerId);
    }, delay);
    
    this.timers.add(timerId);
    return timerId;
  }

  clearTimers(component) {
    this.timers.forEach(timerId => {
      clearTimeout(timerId);
      this.timers.delete(timerId);
    });
  }

  // 对象池模式减少垃圾回收
  createObjectPool(createFn, resetFn, initialSize = 10) {
    const pool = [];
    
    // 预创建对象
    for (let i = 0; i < initialSize; i++) {
      pool.push(createFn());
    }
    
    return {
      get() {
        return pool.length > 0 ? pool.pop() : createFn();
      },
      
      release(obj) {
        resetFn(obj);
        pool.push(obj);
      }
    };
  }
}

CSS 性能优化

1. 减少重绘和回流

CSS 的变化会触发重绘或回流,影响渲染性能:

/* 避免频繁的布局变化 */
.animation-optimized {
  /* 使用 transform 和 opacity,只触发合成层 */
  transform: translateX(0);
  opacity: 1;
  transition: transform 0.3s ease, opacity 0.3s ease;
}

.animation-optimized:hover {
  /* 只改变 transform,不触发回流 */
  transform: translateX(10px);
}

/* 避免的写法 - 会触发回流 */
.animation-bad {
  left: 0;
  transition: left 0.3s ease;
}

.animation-bad:hover {
  left: 10px; /* 改变位置属性会触发回流 */
}
// JavaScript 中的性能优化
class DOMOptimizer {
  constructor() {
    this.batchedUpdates = [];
    this.updateScheduled = false;
  }

  // 批量DOM更新
  batchUpdate(updateFn) {
    this.batchedUpdates.push(updateFn);
    
    if (!this.updateScheduled) {
      this.updateScheduled = true;
      requestAnimationFrame(() => {
        // 批量执行所有更新
        this.batchedUpdates.forEach(update => update());
        this.batchedUpdates = [];
        this.updateScheduled = false;
      });
    }
  }

  // 虚拟滚动实现
  createVirtualScroller(container, items, itemHeight) {
    const visibleCount = Math.ceil(container.clientHeight / itemHeight);
    const bufferCount = 5; // 缓冲区大小
    
    let scrollTop = 0;
    let startIndex = 0;
    let endIndex = visibleCount + bufferCount;
    
    const updateVisibleItems = () => {
      const newStartIndex = Math.floor(scrollTop / itemHeight);
      const newEndIndex = Math.min(
        newStartIndex + visibleCount + bufferCount,
        items.length
      );
      
      if (newStartIndex !== startIndex || newEndIndex !== endIndex) {
        startIndex = newStartIndex;
        endIndex = newEndIndex;
        
        this.renderVisibleItems(container, items, startIndex, endIndex, itemHeight);
      }
    };
    
    container.addEventListener('scroll', (e) => {
      scrollTop = e.target.scrollTop;
      this.batchUpdate(updateVisibleItems);
    });
    
    // 初始渲染
    updateVisibleItems();
  }

  renderVisibleItems(container, items, startIndex, endIndex, itemHeight) {
    const fragment = document.createDocumentFragment();
    
    // 清空容器
    container.innerHTML = '';
    
    // 添加顶部占位符
    const topSpacer = document.createElement('div');
    topSpacer.style.height = `${startIndex * itemHeight}px`;
    fragment.appendChild(topSpacer);
    
    // 渲染可见项
    for (let i = startIndex; i < endIndex; i++) {
      const item = this.createItemElement(items[i], itemHeight);
      fragment.appendChild(item);
    }
    
    // 添加底部占位符
    const bottomSpacer = document.createElement('div');
    bottomSpacer.style.height = `${(items.length - endIndex) * itemHeight}px`;
    fragment.appendChild(bottomSpacer);
    
    container.appendChild(fragment);
  }
}

资源加载优化

图片优化策略

图片通常占据网页资源的60-70%,优化图片是提升性能的关键:

// 智能图片加载管理器
class ImageOptimizer {
  constructor() {
    this.lazyImages = new Set();
    this.observer = null;
    this.initIntersectionObserver();
  }

  initIntersectionObserver() {
    this.observer = new IntersectionObserver((entries) => {
      entries.forEach(entry => {
        if (entry.isIntersecting) {
          this.loadImage(entry.target);
          this.observer.unobserve(entry.target);
        }
      });
    }, {
      rootMargin: '50px' // 提前50px开始加载
    });
  }

  // 响应式图片加载
  loadImage(img) {
    const src = this.getOptimalImageSrc(img);
    
    // 使用 WebP 格式(如果支持)
    if (this.supportsWebP()) {
      const webpSrc = src.replace(/\.(jpg|jpeg|png)$/, '.webp');
      this.loadWithFallback(img, webpSrc, src);
    } else {
      img.src = src;
    }
  }

  getOptimalImageSrc(img) {
    const devicePixelRatio = window.devicePixelRatio || 1;
    const containerWidth = img.parentElement.clientWidth;
    const optimalWidth = Math.ceil(containerWidth * devicePixelRatio);
    
    // 根据屏幕尺寸选择合适的图片
    const baseSrc = img.dataset.src;
    return `${baseSrc}?w=${optimalWidth}&q=85&f=auto`;
  }

  loadWithFallback(img, primarySrc, fallbackSrc) {
    const testImg = new Image();
    
    testImg.onload = () => {
      img.src = primarySrc;
      img.classList.add('loaded');
    };
    
    testImg.onerror = () => {
      img.src = fallbackSrc;
      img.classList.add('loaded');
    };
    
    testImg.src = primarySrc;
  }

  supportsWebP() {
    const canvas = document.createElement('canvas');
    canvas.width = 1;
    canvas.height = 1;
    return canvas.toDataURL('image/webp').startsWith('data:image/webp');
  }

  // 图片预加载
  preloadCriticalImages(imageSrcs) {
    const promises = imageSrcs.map(src => {
      return new Promise((resolve, reject) => {
        const img = new Image();
        img.onload = resolve;
        img.onerror = reject;
        img.src = src;
      });
    });
    
    return Promise.allSettled(promises);
  }

  // 注册懒加载图片
  registerLazyImage(img) {
    this.lazyImages.add(img);
    this.observer.observe(img);
  }
}

// 使用示例
const imageOptimizer = new ImageOptimizer();

// 页面加载完成后初始化
document.addEventListener('DOMContentLoaded', () => {
  // 预加载关键图片
  const criticalImages = [
    '/images/hero-banner.webp',
    '/images/logo.webp'
  ];
  
  imageOptimizer.preloadCriticalImages(criticalImages);
  
  // 注册懒加载图片
  document.querySelectorAll('img[data-src]').forEach(img => {
    imageOptimizer.registerLazyImage(img);
  });
});

代码分割与懒加载

现代前端应用往往包含大量代码,合理的代码分割能显著提升首屏加载速度:

// 路由级别的代码分割
class RouteManager {
  constructor() {
    this.routes = new Map();
    this.loadedChunks = new Set();
    this.loadingPromises = new Map();
  }

  // 注册路由
  registerRoute(path, importFn) {
    this.routes.set(path, {
      import: importFn,
      loaded: false,
      component: null
    });
  }

  // 动态加载路由组件
  async loadRoute(path) {
    const route = this.routes.get(path);
    if (!route) {
      throw new Error(`Route not found: ${path}`);
    }

    // 如果已经加载,直接返回
    if (route.loaded) {
      return route.component;
    }

    // 如果正在加载,返回现有的 Promise
    if (this.loadingPromises.has(path)) {
      return this.loadingPromises.get(path);
    }

    // 开始加载
    const loadPromise = this.loadRouteComponent(route);
    this.loadingPromises.set(path, loadPromise);

    try {
      const component = await loadPromise;
      route.component = component;
      route.loaded = true;
      return component;
    } finally {
      this.loadingPromises.delete(path);
    }
  }

  async loadRouteComponent(route) {
    try {
      // 显示加载状态
      this.showLoadingState();
      
      const module = await route.import();
      
      // 预加载相关资源
      await this.preloadRouteAssets(module);
      
      return module.default;
    } catch (error) {
      console.error('路由加载失败:', error);
      throw error;
    } finally {
      this.hideLoadingState();
    }
  }

  // 预加载相关资源
  async preloadRouteAssets(module) {
    if (module.preloadAssets) {
      const assets = module.preloadAssets();
      await Promise.allSettled(assets.map(asset => this.preloadAsset(asset)));
    }
  }

  preloadAsset(asset) {
    return new Promise((resolve, reject) => {
      if (asset.type === 'script') {
        const script = document.createElement('script');
        script.src = asset.src;
        script.onload = resolve;
        script.onerror = reject;
        document.head.appendChild(script);
      } else if (asset.type === 'style') {
        const link = document.createElement('link');
        link.rel = 'stylesheet';
        link.href = asset.src;
        link.onload = resolve;
        link.onerror = reject;
        document.head.appendChild(link);
      }
    });
  }

  // 路由预加载策略
  setupRoutePreloading() {
    // 鼠标悬停时预加载
    document.addEventListener('mouseover', (e) => {
      const link = e.target.closest('a[data-route]');
      if (link) {
        const route = link.dataset.route;
        this.preloadRoute(route);
      }
    });

    // 空闲时预加载重要路由
    if ('requestIdleCallback' in window) {
      requestIdleCallback(() => {
        this.preloadImportantRoutes();
      });
    }
  }

  async preloadRoute(path) {
    try {
      await this.loadRoute(path);
    } catch (error) {
      // 预加载失败不影响正常流程
      console.warn('路由预加载失败:', path, error);
    }
  }

  preloadImportantRoutes() {
    const importantRoutes = ['/dashboard', '/profile', '/settings'];
    importantRoutes.forEach(route => {
      if (this.routes.has(route)) {
        this.preloadRoute(route);
      }
    });
  }

  showLoadingState() {
    document.body.classList.add('route-loading');
  }

  hideLoadingState() {
    document.body.classList.remove('route-loading');
  }
}

// 使用示例
const routeManager = new RouteManager();

// 注册路由
routeManager.registerRoute('/', () => import('./pages/Home.js'));
routeManager.registerRoute('/about', () => import('./pages/About.js'));
routeManager.registerRoute('/dashboard', () => import('./pages/Dashboard.js'));

// 设置预加载策略
routeManager.setupRoutePreloading();

缓存策略优化

Service Worker 缓存

Service Worker 为我们提供了强大的缓存能力:

// service-worker.js
class CacheManager {
  constructor() {
    this.CACHE_NAME = 'app-cache-v1';
    this.STATIC_CACHE = 'static-cache-v1';
    this.DYNAMIC_CACHE = 'dynamic-cache-v1';
    
    this.STATIC_FILES = [
      '/',
      '/css/main.css',
      '/js/main.js',
      '/images/logo.png'
    ];
  }

  async install() {
    const cache = await caches.open(this.STATIC_CACHE);
    return cache.addAll(this.STATIC_FILES);
  }

  async activate() {
    const cacheNames = await caches.keys();
    const deletePromises = cacheNames
      .filter(name => name !== this.STATIC_CACHE && name !== this.DYNAMIC_CACHE)
      .map(name => caches.delete(name));
    
    return Promise.all(deletePromises);
  }

  async handleFetch(event) {
    const request = event.request;
    const url = new URL(request.url);

    // 静态资源缓存优先
    if (this.isStaticAsset(url)) {
      return this.cacheFirst(request);
    }

    // API 请求网络优先
    if (this.isAPIRequest(url)) {
      return this.networkFirst(request);
    }

    // 页面请求 stale-while-revalidate
    if (this.isPageRequest(request)) {
      return this.staleWhileRevalidate(request);
    }

    // 默认策略
    return fetch(request);
  }

  async cacheFirst(request) {
    const cached = await caches.match(request);
    if (cached) {
      return cached;
    }

    try {
      const response = await fetch(request);
      if (response.ok) {
        const cache = await caches.open(this.STATIC_CACHE);
        cache.put(request, response.clone());
      }
      return response;
    } catch (error) {
      console.error('Cache first strategy failed:', error);
      throw error;
    }
  }

  async networkFirst(request) {
    try {
      const response = await fetch(request);
      if (response.ok) {
        const cache = await caches.open(this.DYNAMIC_CACHE);
        cache.put(request, response.clone());
      }
      return response;
    } catch (error) {
      const cached = await caches.match(request);
      if (cached) {
        return cached;
      }
      throw error;
    }
  }

  async staleWhileRevalidate(request) {
    const cached = await caches.match(request);
    
    const fetchPromise = fetch(request).then(response => {
      if (response.ok) {
        const cache = caches.open(this.DYNAMIC_CACHE);
        cache.then(c => c.put(request, response.clone()));
      }
      return response;
    });

    return cached || fetchPromise;
  }

  isStaticAsset(url) {
    return /\.(css|js|png|jpg|jpeg|gif|svg|woff|woff2)$/.test(url.pathname);
  }

  isAPIRequest(url) {
    return url.pathname.startsWith('/api/');
  }

  isPageRequest(request) {
    return request.method === 'GET' && request.headers.get('accept').includes('text/html');
  }
}

const cacheManager = new CacheManager();

self.addEventListener('install', (event) => {
  event.waitUntil(cacheManager.install());
});

self.addEventListener('activate', (event) => {
  event.waitUntil(cacheManager.activate());
});

self.addEventListener('fetch', (event) => {
  event.respondWith(cacheManager.handleFetch(event));
});

网络优化策略

HTTP/2 和资源优化

// 资源优化管理器
class ResourceOptimizer {
  constructor() {
    this.criticalResources = new Set();
    this.preloadQueue = [];
    this.connectionPool = new Map();
  }

  // 关键资源预加载
  preloadCriticalResources(resources) {
    resources.forEach(resource => {
      const link = document.createElement('link');
      link.rel = 'preload';
      link.href = resource.url;
      link.as = resource.type;
      
      if (resource.type === 'font') {
        link.crossOrigin = 'anonymous';
      }
      
      document.head.appendChild(link);
      this.criticalResources.add(resource.url);
    });
  }

  // DNS 预解析
  preconnectToDomains(domains) {
    domains.forEach(domain => {
      const link = document.createElement('link');
      link.rel = 'preconnect';
      link.href = domain;
      link.crossOrigin = 'anonymous';
      document.head.appendChild(link);
    });
  }

  // 资源压缩检查
  checkResourceCompression() {
    const resources = performance.getEntriesByType('resource');
    const uncompressedResources = [];

    resources.forEach(resource => {
      if (resource.transferSize > resource.encodedBodySize * 0.9) {
        uncompressedResources.push({
          url: resource.name,
          size: resource.transferSize,
          type: this.getResourceType(resource.name)
        });
      }
    });

    if (uncompressedResources.length > 0) {
      console.warn('发现未压缩的资源:', uncompressedResources);
    }

    return uncompressedResources;
  }

  getResourceType(url) {
    const extension = url.split('.').pop().toLowerCase();
    const typeMap = {
      'js': 'script',
      'css': 'stylesheet',
      'png': 'image',
      'jpg': 'image',
      'jpeg': 'image',
      'gif': 'image',
      'svg': 'image',
      'woff': 'font',
      'woff2': 'font',
      'ttf': 'font'
    };
    return typeMap[extension] || 'other';
  }

  // 资源加载优先级管理
  setPriorityHints() {
    // 为关键资源设置高优先级
    document.querySelectorAll('link[rel="stylesheet"]').forEach(link => {
      if (this.criticalResources.has(link.href)) {
        link.setAttribute('importance', 'high');
      }
    });

    // 为非关键图片设置低优先级
    document.querySelectorAll('img[data-src]').forEach(img => {
      img.setAttribute('importance', 'low');
    });
  }

  // 监控资源加载性能
  monitorResourceLoading() {
    const observer = new PerformanceObserver((list) => {
      const entries = list.getEntries();
      entries.forEach(entry => {
        if (entry.duration > 1000) { // 加载时间超过1秒
          console.warn('慢速资源:', {
            url: entry.name,
            duration: entry.duration,
            size: entry.transferSize
          });
        }
      });
    });

    observer.observe({ entryTypes: ['resource'] });
  }
}

// 使用示例
const optimizer = new ResourceOptimizer();

// 预加载关键资源
optimizer.preloadCriticalResources([
  { url: '/css/critical.css', type: 'style' },
  { url: '/js/main.js', type: 'script' },
  { url: '/fonts/main.woff2', type: 'font' }
]);

// 预连接到外部域名
optimizer.preconnectToDomains([
  'https://fonts.googleapis.com',
  'https://api.example.com',
  'https://cdn.example.com'
]);

// 开始监控
optimizer.monitorResourceLoading();

性能监控与分析

实时性能监控

// 性能监控和分析系统
class PerformanceAnalyzer {
  constructor() {
    this.metrics = {
      navigation: {},
      resources: [],
      userInteractions: [],
      errors: []
    };
    this.thresholds = {
      lcp: 2500,
      fid: 100,
      cls: 0.1,
      ttfb: 600
    };
    this.initMonitoring();
  }

  initMonitoring() {
    this.monitorNavigation();
    this.monitorResources();
    this.monitorUserInteractions();
    this.monitorErrors();
    this.setupReporting();
  }

  monitorNavigation() {
    // 监控导航时间
    window.addEventListener('load', () => {
      const navigation = performance.getEntriesByType('navigation')[0];
      this.metrics.navigation = {
        dns: navigation.domainLookupEnd - navigation.domainLookupStart,
        tcp: navigation.connectEnd - navigation.connectStart,
        ttfb: navigation.responseStart - navigation.requestStart,
        domContentLoaded: navigation.domContentLoadedEventEnd - navigation.navigationStart,
        loadComplete: navigation.loadEventEnd - navigation.navigationStart
      };

      this.analyzeNavigationPerformance();
    });
  }

  monitorResources() {
    const observer = new PerformanceObserver((list) => {
      const entries = list.getEntries();
      entries.forEach(entry => {
        this.metrics.resources.push({
          name: entry.name,
          type: entry.initiatorType,
          size: entry.transferSize,
          duration: entry.duration,
          startTime: entry.startTime
        });
      });
    });

    observer.observe({ entryTypes: ['resource'] });
  }

  monitorUserInteractions() {
    // 监控点击响应时间
    document.addEventListener('click', (event) => {
      const startTime = performance.now();
      
      requestAnimationFrame(() => {
        const endTime = performance.now();
        const responseTime = endTime - startTime;
        
        this.metrics.userInteractions.push({
          type: 'click',
          target: event.target.tagName,
          responseTime,
          timestamp: Date.now()
        });

        if (responseTime > this.thresholds.fid) {
          this.reportSlowInteraction(event.target, responseTime);
        }
      });
    });
  }

  monitorErrors() {
    // 监控JavaScript错误
    window.addEventListener('error', (event) => {
      this.metrics.errors.push({
        type: 'javascript',
        message: event.message,
        filename: event.filename,
        line: event.lineno,
        column: event.colno,
        timestamp: Date.now()
      });
    });

    // 监控资源加载错误
    window.addEventListener('error', (event) => {
      if (event.target !== window) {
        this.metrics.errors.push({
          type: 'resource',
          element: event.target.tagName,
          source: event.target.src || event.target.href,
          timestamp: Date.now()
        });
      }
    }, true);
  }

  analyzeNavigationPerformance() {
    const nav = this.metrics.navigation;
    const issues = [];

    if (nav.dns > 200) {
      issues.push('DNS解析时间过长');
    }

    if (nav.tcp > 500) {
      issues.push('TCP连接时间过长');
    }

    if (nav.ttfb > this.thresholds.ttfb) {
      issues.push('服务器响应时间过长');
    }

    if (issues.length > 0) {
      console.warn('性能问题:', issues);
      this.reportPerformanceIssues(issues);
    }
  }

  reportSlowInteraction(target, responseTime) {
    console.warn('慢速交互:', {
      element: target.tagName,
      responseTime: `${responseTime.toFixed(2)}ms`,
      threshold: `${this.thresholds.fid}ms`
    });
  }

  reportPerformanceIssues(issues) {
    // 发送性能问题报告
    fetch('/api/performance-issues', {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({
        issues,
        metrics: this.metrics,
        userAgent: navigator.userAgent,
        timestamp: Date.now()
      })
    }).catch(err => console.error('性能报告发送失败:', err));
  }

  setupReporting() {
    // 定期发送性能报告
    setInterval(() => {
      this.sendPerformanceReport();
    }, 30000); // 每30秒发送一次

    // 页面卸载时发送报告
    window.addEventListener('beforeunload', () => {
      this.sendPerformanceReport();
    });
  }

  sendPerformanceReport() {
    const report = {
      metrics: this.metrics,
      url: window.location.href,
      timestamp: Date.now(),
      connection: this.getConnectionInfo()
    };

    // 使用 sendBeacon 确保数据能够发送
    if (navigator.sendBeacon) {
      navigator.sendBeacon('/api/performance', JSON.stringify(report));
    } else {
      fetch('/api/performance', {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify(report)
      }).catch(err => console.error('性能报告发送失败:', err));
    }
  }

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

  // 生成性能报告
  generateReport() {
    const report = {
      summary: this.generateSummary(),
      details: this.metrics,
      recommendations: this.generateRecommendations()
    };

    console.table(report.summary);
    return report;
  }

  generateSummary() {
    const nav = this.metrics.navigation;
    const resources = this.metrics.resources;
    
    return {
      '首次字节时间': `${nav.ttfb || 0}ms`,
      'DOM加载完成': `${nav.domContentLoaded || 0}ms`,
      '页面加载完成': `${nav.loadComplete || 0}ms`,
      '资源总数': resources.length,
      '资源总大小': this.formatBytes(resources.reduce((sum, r) => sum + r.size, 0)),
      '交互次数': this.metrics.userInteractions.length,
      '错误数量': this.metrics.errors.length
    };
  }

  generateRecommendations() {
    const recommendations = [];
    const nav = this.metrics.navigation;
    const resources = this.metrics.resources;

    if (nav.ttfb > this.thresholds.ttfb) {
      recommendations.push('优化服务器响应时间');
    }

    const largeResources = resources.filter(r => r.size > 1024 * 1024);
    if (largeResources.length > 0) {
      recommendations.push('压缩大型资源文件');
    }

    const slowResources = resources.filter(r => r.duration > 1000);
    if (slowResources.length > 0) {
      recommendations.push('优化慢速加载的资源');
    }

    return recommendations;
  }

  formatBytes(bytes) {
    if (bytes === 0) return '0 B';
    const k = 1024;
    const sizes = ['B', 'KB', 'MB', 'GB'];
    const i = Math.floor(Math.log(bytes) / Math.log(k));
    return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i];
  }
}

// 初始化性能监控
const analyzer = new PerformanceAnalyzer();

// 在控制台中查看性能报告
window.getPerformanceReport = () => analyzer.generateReport();

总结

前端性能优化是一个系统工程,需要从多个维度协同优化:

关键要点:

  1. 建立性能监控体系 - 没有度量就没有优化的基础
  2. 代码层面优化 - JavaScript执行效率和CSS渲染性能
  3. 资源加载优化 - 图片、字体、脚本的智能加载策略
  4. 缓存策略 - 利用浏览器缓存和Service Worker
  5. 网络优化 - HTTP/2、资源压缩、CDN加速

实施建议:

  • 从测量开始,建立性能基线
  • 优先解决影响用户体验的关键问题
  • 采用渐进式优化策略,避免过度优化
  • 建立持续监控和改进机制

性能优化没有银弹,但通过系统性的方法和持续的监控,我们可以为用户提供更好的体验。记住,每减少100ms的加载时间,都可能带来1%的转化率提升。

你可能感兴趣的:(前端,性能优化,web)