前端老旧项目全面性能优化指南与面试攻略

目录

  1. 项目性能分析与诊断
  2. 代码层面优化
  3. 资源加载优化
  4. 运行时性能优化
  5. 网络请求优化
  6. 构建与部署优化
  7. 监控与持续优化
  8. 面试回答技巧
  9. 实战案例分析

项目性能分析与诊断

1. 性能指标了解

核心 Web Vitals

  • LCP (Largest Contentful Paint): 最大内容绘制时间

    • 良好: ≤ 2.5s
    • 需要改进: 2.5s - 4s
    • 差: > 4s
  • FID (First Input Delay): 首次输入延迟

    • 良好: ≤ 100ms
    • 需要改进: 100ms - 300ms
    • 差: > 300ms
  • CLS (Cumulative Layout Shift): 累积布局偏移

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

其他重要指标

  • FCP (First Contentful Paint): 首次内容绘制
  • TTI (Time to Interactive): 可交互时间
  • TBT (Total Blocking Time): 总阻塞时间
  • SI (Speed Index): 速度指数

2. 性能分析工具使用

浏览器开发者工具

// Performance API 分析
const observer = new PerformanceObserver((list) => {
  list.getEntries().forEach((entry) => {
    console.log('Performance entry:', entry);
  });
});

observer.observe({
  entryTypes: ['navigation', 'resource', 'paint', 'largest-contentful-paint']
});

// 手动标记关键时间点
performance.mark('component-start');
// ... 组件渲染逻辑
performance.mark('component-end');
performance.measure('component-render', 'component-start', 'component-end');

Lighthouse 自动化分析

// lighthouse-ci.js 配置
module.exports = {
  ci: {
    collect: {
      url: ['http://localhost:3000'],
      numberOfRuns: 3,
    },
    assert: {
      assertions: {
        'categories:performance': ['warn', { minScore: 0.9 }],
        'categories:accessibility': ['error', { minScore: 0.9 }],
      },
    },
    upload: {
      target: 'lhci',
      serverBaseUrl: 'https://your-lhci-server.com',
    },
  },
};

WebPageTest 分析

  • 多地区测试
  • 真实设备测试
  • 网络条件模拟
  • 详细的瀑布图分析

3. 性能问题诊断清单

加载性能问题

  • 资源文件过大(JS、CSS、图片)
  • 关键资源未优先加载
  • 阻塞渲染的资源
  • 网络请求过多
  • 缓存策略不合理

运行时性能问题

  • JavaScript 执行时间过长
  • 频繁的 DOM 操作
  • 内存泄漏
  • 未优化的事件监听器
  • 重复的计算或渲染

代码层面优化

1. JavaScript 优化

代码分割与懒加载

// 路由级别的代码分割
const routes = [
  {
    path: '/home',
    component: () => import('./views/Home.vue')
  },
  {
    path: '/profile',
    component: () => import('./views/Profile.vue')
  }
];

// 组件级别的懒加载
const LazyComponent = React.lazy(() => import('./LazyComponent'));

function App() {
  return (
    <Suspense fallback={<div>Loading...</div>}>
      <LazyComponent />
    </Suspense>
  );
}

避免阻塞主线程

// 使用 requestIdleCallback 进行任务调度
function performHeavyTask(data) {
  const chunks = chunkArray(data, 100);
  
  function processChunk(index = 0) {
    if (index >= chunks.length) return;
    
    requestIdleCallback((deadline) => {
      while (deadline.timeRemaining() > 0 && index < chunks.length) {
        processDataChunk(chunks[index]);
        index++;
      }
      
      if (index < chunks.length) {
        processChunk(index);
      }
    });
  }
  
  processChunk();
}

// Web Workers 处理CPU密集型任务
// main.js
const worker = new Worker('calculation-worker.js');
worker.postMessage({ data: largeDataSet });
worker.onmessage = (e) => {
  console.log('计算结果:', e.data);
};

// calculation-worker.js
self.onmessage = function(e) {
  const result = heavyCalculation(e.data);
  self.postMessage(result);
};

内存优化

// 防止内存泄漏
class ComponentManager {
  constructor() {
    this.timers = new Set();
    this.observers = new Set();
    this.eventListeners = new Map();
  }
  
  addTimer(id) {
    this.timers.add(id);
  }
  
  addObserver(observer) {
    this.observers.add(observer);
  }
  
  addEventListener(element, event, handler) {
    element.addEventListener(event, handler);
    if (!this.eventListeners.has(element)) {
      this.eventListeners.set(element, []);
    }
    this.eventListeners.get(element).push({ event, handler });
  }
  
  cleanup() {
    // 清理定时器
    this.timers.forEach(id => clearInterval(id));
    this.timers.clear();
    
    // 清理观察者
    this.observers.forEach(observer => observer.disconnect());
    this.observers.clear();
    
    // 清理事件监听器
    this.eventListeners.forEach((events, element) => {
      events.forEach(({ event, handler }) => {
        element.removeEventListener(event, handler);
      });
    });
    this.eventListeners.clear();
  }
}

2. CSS 优化

关键 CSS 提取

// 使用 critical 包提取关键CSS
const critical = require('critical');

critical.generate({
  inline: true,
  base: 'dist/',
  src: 'index.html',
  target: {
    css: 'critical.css',
    html: 'index-critical.html'
  },
  width: 1300,
  height: 900,
  minify: true
});

CSS 优化技巧

/* 避免深层嵌套和复杂选择器 */
/* 不好的做法 */
.header .nav .menu li a:hover {
  color: blue;
}

/* 好的做法 */
.nav-link:hover {
  color: blue;
}

/* 使用 CSS containment 优化渲染性能 */
.card {
  contain: layout style paint;
}

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

.animated-element.done {
  will-change: auto; /* 动画完成后移除 */
}

/* 使用 CSS 变量减少重复计算 */
:root {
  --primary-color: #007bff;
  --border-radius: 4px;
  --shadow: 0 2px 4px rgba(0,0,0,0.1);
}

CSS 加载优化

<!-- 预加载关键字体 -->
<link rel="preload" href="/fonts/main.woff2" as="font" type="font/woff2" crossorigin>

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

资源加载优化

1. 图片优化

现代图片格式

<!-- 使用 picture 元素支持多种格式 -->
<picture>
  <source srcset="image.avif" type="image/avif">
  <source srcset="image.webp" type="image/webp">
  <img src="image.jpg" alt="描述" loading="lazy">
</picture>

响应式图片

<!-- 基于视口宽度的响应式图片 -->
<img src="small.jpg"
     srcset="small.jpg 480w, medium.jpg 800w, large.jpg 1200w"
     sizes="(max-width: 480px) 100vw, (max-width: 800px) 50vw, 33vw"
     alt="响应式图片">

图片懒加载实现

// 使用 Intersection Observer 实现懒加载
class LazyImageLoader {
  constructor() {
    this.imageObserver = new IntersectionObserver(
      this.onImageIntersection.bind(this),
      { rootMargin: '50px' }
    );
    
    this.init();
  }
  
  init() {
    const lazyImages = document.querySelectorAll('img[data-src]');
    lazyImages.forEach(img => this.imageObserver.observe(img));
  }
  
  onImageIntersection(entries) {
    entries.forEach(entry => {
      if (entry.isIntersecting) {
        this.loadImage(entry.target);
        this.imageObserver.unobserve(entry.target);
      }
    });
  }
  
  loadImage(img) {
    img.src = img.dataset.src;
    img.onload = () => img.classList.add('loaded');
    img.onerror = () => img.classList.add('error');
  }
}

new LazyImageLoader();

2. 字体优化

字体加载策略

/* 字体显示策略 */
@font-face {
  font-family: 'CustomFont';
  src: url('/fonts/custom.woff2') format('woff2');
  font-display: swap; /* 立即显示后备字体 */
}

/* 字体预加载 */
<link rel="preload" href="/fonts/critical.woff2" as="font" type="font/woff2" crossorigin>

字体子集化

// 使用 fonttools 进行字体子集化
const fontSubset = require('font-subset');

fontSubset({
  font: 'source-font.ttf',
  text: '需要的文字内容',
  output: 'subset-font.woff2'
});

3. 资源预加载策略

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

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

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

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

<!-- 模块预加载 -->
<link rel="modulepreload" href="/modules/app.js">

运行时性能优化

1. DOM 操作优化

批量 DOM 操作

// 使用 DocumentFragment 减少重排重绘
function batchUpdateDOM(items) {
  const fragment = document.createDocumentFragment();
  
  items.forEach(item => {
    const element = document.createElement('div');
    element.textContent = item.text;
    element.className = item.className;
    fragment.appendChild(element);
  });
  
  // 一次性插入所有元素
  document.getElementById('container').appendChild(fragment);
}

// 使用 requestAnimationFrame 优化动画
function animateElement(element, from, to, duration) {
  const start = performance.now();
  
  function update(currentTime) {
    const elapsed = currentTime - start;
    const progress = Math.min(elapsed / duration, 1);
    
    const current = from + (to - from) * progress;
    element.style.transform = `translateX(${current}px)`;
    
    if (progress < 1) {
      requestAnimationFrame(update);
    }
  }
  
  requestAnimationFrame(update);
}

虚拟滚动实现

class VirtualScroller {
  constructor(container, itemHeight, items) {
    this.container = container;
    this.itemHeight = itemHeight;
    this.items = items;
    this.visibleCount = Math.ceil(container.clientHeight / itemHeight) + 2;
    this.startIndex = 0;
    
    this.init();
  }
  
  init() {
    this.container.style.height = `${this.items.length * this.itemHeight}px`;
    this.container.addEventListener('scroll', this.onScroll.bind(this));
    this.render();
  }
  
  onScroll() {
    const scrollTop = this.container.scrollTop;
    const newStartIndex = Math.floor(scrollTop / this.itemHeight);
    
    if (newStartIndex !== this.startIndex) {
      this.startIndex = newStartIndex;
      this.render();
    }
  }
  
  render() {
    const endIndex = Math.min(
      this.startIndex + this.visibleCount,
      this.items.length
    );
    
    const visibleItems = this.items.slice(this.startIndex, endIndex);
    
    this.container.innerHTML = visibleItems.map((item, index) => `
      <div class="item" style="
        position: absolute;
        top: ${(this.startIndex + index) * this.itemHeight}px;
        height: ${this.itemHeight}px;
      ">
        ${item.content}
      </div>
    `).join('');
  }
}

2. 事件优化

事件委托

// 使用事件委托减少事件监听器数量
class EventDelegator {
  constructor(container) {
    this.container = container;
    this.handlers = new Map();
    
    this.container.addEventListener('click', this.handleClick.bind(this));
  }
  
  register(selector, handler) {
    if (!this.handlers.has(selector)) {
      this.handlers.set(selector, []);
    }
    this.handlers.get(selector).push(handler);
  }
  
  handleClick(event) {
    this.handlers.forEach((handlers, selector) => {
      if (event.target.matches(selector)) {
        handlers.forEach(handler => handler(event));
      }
    });
  }
}

// 使用示例
const delegator = new EventDelegator(document.body);
delegator.register('.button', (e) => console.log('Button clicked'));
delegator.register('.link', (e) => console.log('Link clicked'));

防抖和节流

// 防抖函数
function debounce(func, wait, immediate = false) {
  let timeout;
  
  return function executedFunction(...args) {
    const later = () => {
      timeout = null;
      if (!immediate) func.apply(this, args);
    };
    
    const callNow = immediate && !timeout;
    clearTimeout(timeout);
    timeout = setTimeout(later, wait);
    
    if (callNow) func.apply(this, args);
  };
}

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

// 使用示例
const debouncedSearch = debounce((query) => {
  // 执行搜索
}, 300);

const throttledScroll = throttle(() => {
  // 处理滚动
}, 100);

window.addEventListener('scroll', throttledScroll);

网络请求优化

1. HTTP 优化

请求合并与缓存

// 请求缓存管理器
class RequestCacheManager {
  constructor() {
    this.cache = new Map();
    this.pendingRequests = new Map();
  }
  
  async get(url, options = {}) {
    const cacheKey = this.getCacheKey(url, options);
    
    // 检查缓存
    if (this.cache.has(cacheKey)) {
      const cached = this.cache.get(cacheKey);
      if (!this.isExpired(cached)) {
        return cached.data;
      }
    }
    
    // 检查是否有pending请求
    if (this.pendingRequests.has(cacheKey)) {
      return this.pendingRequests.get(cacheKey);
    }
    
    // 发起新请求
    const request = this.makeRequest(url, options);
    this.pendingRequests.set(cacheKey, request);
    
    try {
      const data = await request;
      this.cache.set(cacheKey, {
        data,
        timestamp: Date.now(),
        ttl: options.ttl || 60000 // 默认1分钟
      });
      return data;
    } finally {
      this.pendingRequests.delete(cacheKey);
    }
  }
  
  getCacheKey(url, options) {
    return `${url}:${JSON.stringify(options)}`;
  }
  
  isExpired(cached) {
    return Date.now() - cached.timestamp > cached.ttl;
  }
  
  async makeRequest(url, options) {
    const response = await fetch(url, options);
    return response.json();
  }
}

批量请求优化

// 请求批处理器
class BatchRequestProcessor {
  constructor(batchSize = 10, delay = 100) {
    this.batchSize = batchSize;
    this.delay = delay;
    this.queue = [];
    this.processing = false;
  }
  
  add(request) {
    return new Promise((resolve, reject) => {
      this.queue.push({ request, resolve, reject });
      this.process();
    });
  }
  
  async process() {
    if (this.processing || this.queue.length === 0) return;
    
    this.processing = true;
    
    while (this.queue.length > 0) {
      const batch = this.queue.splice(0, this.batchSize);
      
      try {
        const promises = batch.map(({ request }) => request());
        const results = await Promise.allSettled(promises);
        
        results.forEach((result, index) => {
          const { resolve, reject } = batch[index];
          if (result.status === 'fulfilled') {
            resolve(result.value);
          } else {
            reject(result.reason);
          }
        });
      } catch (error) {
        batch.forEach(({ reject }) => reject(error));
      }
      
      // 批次间延迟
      if (this.queue.length > 0) {
        await new Promise(resolve => setTimeout(resolve, this.delay));
      }
    }
    
    this.processing = false;
  }
}

2. 数据预加载

智能预加载策略

// 路由预加载器
class RoutePreloader {
  constructor() {
    this.preloadedRoutes = new Set();
    this.observer = new IntersectionObserver(
      this.onLinkVisible.bind(this),
      { rootMargin: '100px' }
    );
    
    this.init();
  }
  
  init() {
    // 监听所有路由链接
    const links = document.querySelectorAll('a[href^="/"]');
    links.forEach(link => this.observer.observe(link));
    
    // 鼠标悬停预加载
    document.addEventListener('mouseover', this.onLinkHover.bind(this));
  }
  
  onLinkVisible(entries) {
    entries.forEach(entry => {
      if (entry.isIntersecting) {
        const href = entry.target.getAttribute('href');
        this.preloadRoute(href);
      }
    });
  }
  
  onLinkHover(event) {
    if (event.target.tagName === 'A' && event.target.href) {
      const href = new URL(event.target.href).pathname;
      this.preloadRoute(href);
    }
  }
  
  async preloadRoute(href) {
    if (this.preloadedRoutes.has(href)) return;
    
    this.preloadedRoutes.add(href);
    
    try {
      // 预加载路由组件
      const module = await import(/* webpackChunkName: "[request]" */ `./views${href}`);
      console.log(`预加载路由: ${href}`);
    } catch (error) {
      console.warn(`预加载路由失败: ${href}`, error);
      this.preloadedRoutes.delete(href);
    }
  }
}

构建与部署优化

1. Webpack 构建优化

配置优化示例

// webpack.config.js
const path = require('path');
const TerserPlugin = require('terser-webpack-plugin');
const CompressionPlugin = require('compression-webpack-plugin');
const { BundleAnalyzerPlugin } = require('webpack-bundle-analyzer');

module.exports = {
  mode: 'production',
  
  // 入口优化
  entry: {
    app: './src/main.js',
    vendor: ['react', 'react-dom', 'lodash'] // 提取第三方库
  },
  
  // 输出优化
  output: {
    path: path.resolve(__dirname, 'dist'),
    filename: '[name].[contenthash:8].js',
    chunkFilename: '[name].[contenthash:8].chunk.js',
    clean: true
  },
  
  // 优化配置
  optimization: {
    // 代码分割
    splitChunks: {
      chunks: 'all',
      cacheGroups: {
        vendor: {
          test: /[\\/]node_modules[\\/]/,
          name: 'vendors',
          chunks: 'all',
          priority: 10
        },
        common: {
          name: 'common',
          minChunks: 2,
          chunks: 'all',
          priority: 5
        }
      }
    },
    
    // 压缩优化
    minimizer: [
      new TerserPlugin({
        terserOptions: {
          compress: {
            drop_console: true, // 移除console
            drop_debugger: true // 移除debugger
          }
        },
        parallel: true // 并行压缩
      })
    ],
    
    // 运行时代码提取
    runtimeChunk: 'single'
  },
  
  // 插件配置
  plugins: [
    // Gzip压缩
    new CompressionPlugin({
      algorithm: 'gzip',
      test: /\.(js|css|html|svg)$/,
      threshold: 8192,
      minRatio: 0.8
    }),
    
    // 包分析
    process.env.ANALYZE && new BundleAnalyzerPlugin()
  ].filter(Boolean),
  
  // 模块解析优化
  resolve: {
    alias: {
      '@': path.resolve(__dirname, 'src')
    },
    extensions: ['.js', '.jsx', '.ts', '.tsx'],
    modules: ['node_modules'] // 限制模块查找范围
  },
  
  // 外部化依赖
  externals: {
    'react': 'React',
    'react-dom': 'ReactDOM'
  }
};

2. 现代构建工具优化

Vite 配置优化

// vite.config.js
import { defineConfig } from 'vite';
import { resolve } from 'path';

export default defineConfig({
  // 构建优化
  build: {
    target: 'es2015',
    minify: 'terser',
    terserOptions: {
      compress: {
        drop_console: true,
        drop_debugger: true
      }
    },
    
    // 代码分割
    rollupOptions: {
      output: {
        manualChunks: {
          vendor: ['react', 'react-dom'],
          utils: ['lodash', 'dayjs']
        }
      }
    },
    
    // 资源大小限制
    chunkSizeWarningLimit: 1000
  },
  
  // 开发服务器优化
  server: {
    hmr: {
      overlay: false // 关闭错误遮罩层
    }
  },
  
  // 插件配置
  plugins: [
    // ... 插件配置
  ],
  
  // 依赖预构建
  optimizeDeps: {
    include: ['react', 'react-dom'],
    exclude: ['@vueuse/core']
  }
});

3. CDN 与缓存策略

缓存策略配置

# nginx 配置示例
server {
    listen 80;
    server_name example.com;
    
    # 静态资源缓存
    location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg|woff|woff2)$ {
        expires 1y;
        add_header Cache-Control "public, immutable";
        add_header Vary "Accept-Encoding";
        
        # 启用 gzip
        gzip on;
        gzip_types text/plain text/css application/json application/javascript text/xml application/xml application/xml+rss text/javascript;
    }
    
    # HTML 文件缓存策略
    location ~* \.(html|htm)$ {
        expires 1h;
        add_header Cache-Control "public, must-revalidate";
    }
    
    # API 缓存
    location /api/ {
        proxy_pass http://backend;
        proxy_cache my_cache;
        proxy_cache_valid 200 5m;
        proxy_cache_key $request_uri;
    }
}

监控与持续优化

1. 性能监控实现

Real User Monitoring (RUM)

// 性能监控类
class PerformanceMonitor {
  constructor(apiUrl) {
    this.apiUrl = apiUrl;
    this.metrics = {};
    this.init();
  }
  
  init() {
    // 监听页面加载完成
    window.addEventListener('load', () => {
      this.collectPageMetrics();
    });
    
    // 监听 CLS
    this.observeCLS();
    
    // 监听 LCP
    this.observeLCP();
    
    // 监听 FID
    this.observeFID();
    
    // 定期发送数据
    setInterval(() => this.sendMetrics(), 30000);
  }
  
  collectPageMetrics() {
    const navigation = performance.getEntriesByType('navigation')[0];
    
    this.metrics = {
      ...this.metrics,
      dns: navigation.domainLookupEnd - navigation.domainLookupStart,
      tcp: navigation.connectEnd - navigation.connectStart,
      request: navigation.responseStart - navigation.requestStart,
      response: navigation.responseEnd - navigation.responseStart,
      dom: navigation.domContentLoadedEventEnd - navigation.domContentLoadedEventStart,
      load: navigation.loadEventEnd - navigation.loadEventStart,
      fcp: this.getFCP(),
      ttfb: navigation.responseStart - navigation.requestStart
    };
  }
  
  observeCLS() {
    let clsValue = 0;
    let clsEntries = [];
    
    const observer = new PerformanceObserver((list) => {
      for (const entry of list.getEntries()) {
        if (!entry.hadRecentInput) {
          clsValue += entry.value;
          clsEntries.push(entry);
        }
      }
      this.metrics.cls = clsValue;
    });
    
    observer.observe({ type: 'layout-shift', buffered: true });
  }
  
  observeLCP() {
    const observer = new PerformanceObserver((list) => {
      const entries = list.getEntries();
      const lastEntry = entries[entries.length - 1];
      this.metrics.lcp = lastEntry.startTime;
    });
    
    observer.observe({ type: 'largest-contentful-paint', buffered: true });
  }
  
  observeFID() {
    const observer = new PerformanceObserver((list) => {
      for (const entry of list.getEntries()) {
        this.metrics.fid = entry.processingStart - entry.startTime;
      }
    });
    
    observer.observe({ type: 'first-input', buffered: true });
  }
  
  getFCP() {
    const fcpEntry = performance.getEntriesByName('first-contentful-paint')[0];
    return fcpEntry ? fcpEntry.startTime : null;
  }
  
  async sendMetrics() {
    if (Object.keys(this.metrics).length === 0) return;
    
    try {
      await fetch(this.apiUrl, {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({
          url: location.href,
          userAgent: navigator.userAgent,
          timestamp: Date.now(),
          metrics: this.metrics
        })
      });
    } catch (error) {
      console.error('发送监控数据失败:', error);
    }
  }
}

// 初始化监控
new PerformanceMonitor('/api/metrics');

2. 错误监控

错误收集与上报

class ErrorMonitor {
  constructor(apiUrl) {
    this.apiUrl = apiUrl;
    this.errorQueue = [];
    this.maxQueueSize = 10;
    
    this.init();
  }
  
  init() {
    // JavaScript 错误监听
    window.addEventListener('error', this.handleError.bind(this));
    
    // Promise 未捕获错误
    window.addEventListener('unhandledrejection', this.handlePromiseError.bind(this));
    
    // 资源加载错误
    window.addEventListener('error', this.handleResourceError.bind(this), true);
    
    // 定期发送错误
    setInterval(() => this.flushErrors(), 10000);
  }
  
  handleError(event) {
    this.addError({
      type: 'javascript',
      message: event.message,
      filename: event.filename,
      lineno: event.lineno,
      colno: event.colno,
      stack: event.error?.stack,
      timestamp: Date.now()
    });
  }
  
  handlePromiseError(event) {
    this.addError({
      type: 'promise',
      message: event.reason.toString(),
      stack: event.reason.stack,
      timestamp: Date.now()
    });
  }
  
  handleResourceError(event) {
    if (event.target !== window) {
      this.addError({
        type: 'resource',
        message: `Failed to load ${event.target.tagName}`,
        source: event.target.src || event.target.href,
        timestamp: Date.now()
      });
    }
  }
  
  addError(error) {
    this.errorQueue.push(error);
    
    if (this.errorQueue.length >= this.maxQueueSize) {
      this.flushErrors();
    }
  }
  
  async flushErrors() {
    if (this.errorQueue.length === 0) return;
    
    const errors = [...this.errorQueue];
    this.errorQueue = [];
    
    try {
      await fetch(this.apiUrl, {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({
          url: location.href,
          userAgent: navigator.userAgent,
          errors
        })
      });
    } catch (error) {
      // 发送失败,重新加入队列
      this.errorQueue.unshift(...errors);
    }
  }
}

面试回答技巧

1. 回答框架

STAR 方法

  • Situation: 描述项目背景和遇到的性能问题
  • Task: 说明你的任务和目标
  • Action: 详细描述你采取的优化措施
  • Result: 量化优化结果和效果

2. 常见面试问题及回答要点

问题1: 如何分析老旧项目的性能问题?

回答要点:

1. 性能指标分析
   - 使用 Lighthouse 进行综合评估
   - Chrome DevTools 分析加载时间线
   - 监控 Core Web Vitals 指标

2. 代码质量审查
   - 分析 bundle 大小和组成
   - 检查无用代码和重复依赖
   - 评估代码分割情况

3. 网络性能分析
   - 检查资源加载策略
   - 分析缓存配置
   - 评估 CDN 使用情况

4. 运行时性能分析
   - 使用 Performance 面板分析主线程阻塞
   - 检查内存使用和泄漏
   - 分析重排重绘频率

问题2: 具体实施了哪些优化措施?

回答示例:

我负责的项目是一个电商后台管理系统,页面加载时间超过8秒,用户体验很差。

优化措施:

1. 构建优化 (40% 性能提升)
   - 实施代码分割,将第三方库单独打包
   - 启用 Tree Shaking 删除无用代码
   - 配置 Gzip 压缩,资源大小减少60%

2. 资源优化 (30% 性能提升)
   - 图片格式现代化,使用 WebP
   - 实施图片懒加载
   - 字体预加载和子集化

3. 代码优化 (20% 性能提升)
   - 实施虚拟滚动解决长列表性能问题
   - 使用 React.memo 和 useMemo 减少不必要渲染
   - 优化事件处理,使用防抖节流

4. 缓存策略 (10% 性能提升)
   - 配置合理的 HTTP 缓存策略
   - 实施接口缓存和预加载

结果:页面加载时间从8秒降到2.5秒,Lighthouse 分数从30分提升到85分。

问题3: 如何保证优化效果的持续性?

回答要点:

1. 性能监控体系
   - 部署 RUM 监控收集真实用户数据
   - 设置性能预警阈值
   - 定期生成性能报告

2. 开发流程集成
   - CI/CD 中集成 Lighthouse 检查
   - Bundle 分析报告
   - 性能回归测试

3. 团队规范建立
   - 制定性能开发规范
   - 代码审查中关注性能
   - 定期技术分享和培训

4. 持续优化机制
   - 月度性能回顾会议
   - 新技术调研和应用
   - 用户反馈收集和处理

3. 面试加分技巧

展示技术深度

  • 能够解释优化原理,不只是列举方法
  • 提及新技术和最佳实践的应用
  • 展示对浏览器工作原理的理解

展示业务思维

  • 将技术优化与业务价值关联
  • 提及用户体验改善和转化率提升
  • 展示成本效益分析能力

展示团队协作

  • 说明如何推动团队采用新的优化方案
  • 分享知识传递和培训经验
  • 展示跨部门沟通协调能力

实战案例分析

案例1: 电商网站首页优化

优化前状况:

  • 首屏加载时间: 6.8秒
  • Lighthouse 分数: 32分
  • 资源总大小: 3.2MB
  • JavaScript 执行时间: 2.1秒

优化措施:

  1. 关键资源优化
// 首屏关键CSS内联
const criticalCSS = `
  .header { height: 60px; background: #fff; }
  .hero { height: 400px; background: #f5f5f5; }
`;

// 非关键CSS异步加载
const loadCSS = (href) => {
  const link = document.createElement('link');
  link.rel = 'preload';
  link.as = 'style';
  link.href = href;
  link.onload = () => link.rel = 'stylesheet';
  document.head.appendChild(link);
};
  1. 图片优化
<!-- 首屏轮播图优化 -->
<picture>
  <source srcset="hero-mobile.webp" media="(max-width: 768px)" type="image/webp">
  <source srcset="hero-desktop.webp" type="image/webp">
  <img src="hero-desktop.jpg" alt="首页轮播" loading="eager">
</picture>
  1. JavaScript 优化
// 路由懒加载
const routes = [
  {
    path: '/product/:id',
    component: () => import(/* webpackChunkName: "product" */ './Product.vue')
  }
];

// 第三方脚本延迟加载
const loadScript = (src, defer = true) => {
  const script = document.createElement('script');
  script.src = src;
  script.defer = defer;
  document.head.appendChild(script);
};

// 页面加载完成后加载非关键脚本
window.addEventListener('load', () => {
  loadScript('/js/analytics.js');
  loadScript('/js/chat-widget.js');
});

优化结果:

  • 首屏加载时间: 2.1秒 (提升69%)
  • Lighthouse 分数: 89分 (提升178%)
  • 资源总大小: 1.8MB (减少44%)
  • 用户转化率提升15%

案例2: 后台管理系统优化

优化前问题:

  • 列表页渲染1000条数据卡顿严重
  • 内存占用持续增长,存在泄漏
  • 频繁的接口请求导致服务器压力大

优化方案:

  1. 虚拟滚动实现
<template>
  <div class="virtual-list" ref="container" @scroll="onScroll">
    <div class="virtual-list-phantom" :style="{ height: totalHeight + 'px' }"></div>
    <div class="virtual-list-content" :style="getContentStyle()">
      <div
        v-for="item in visibleItems"
        :key="item.id"
        class="virtual-list-item"
        :style="{ height: itemHeight + 'px' }"
      >
        <slot :item="item"></slot>
      </div>
    </div>
  </div>
</template>

<script>
export default {
  props: {
    items: Array,
    itemHeight: { type: Number, default: 50 }
  },
  data() {
    return {
      startIndex: 0,
      endIndex: 0,
      containerHeight: 0
    };
  },
  computed: {
    totalHeight() {
      return this.items.length * this.itemHeight;
    },
    visibleCount() {
      return Math.ceil(this.containerHeight / this.itemHeight) + 2;
    },
    visibleItems() {
      return this.items.slice(this.startIndex, this.endIndex);
    }
  },
  methods: {
    onScroll() {
      const scrollTop = this.$refs.container.scrollTop;
      this.startIndex = Math.floor(scrollTop / this.itemHeight);
      this.endIndex = this.startIndex + this.visibleCount;
    },
    getContentStyle() {
      return {
        transform: `translateY(${this.startIndex * this.itemHeight}px)`
      };
    }
  }
};
</script>
  1. 内存泄漏修复
// 组件清理机制
export default {
  data() {
    return {
      timers: [],
      observers: [],
      subscriptions: []
    };
  },
  
  methods: {
    addTimer(id) {
      this.timers.push(id);
    },
    
    addObserver(observer) {
      this.observers.push(observer);
    },
    
    addSubscription(subscription) {
      this.subscriptions.push(subscription);
    }
  },
  
  beforeDestroy() {
    // 清理定时器
    this.timers.forEach(clearInterval);
    
    // 清理观察者
    this.observers.forEach(observer => observer.disconnect());
    
    // 清理订阅
    this.subscriptions.forEach(sub => sub.unsubscribe());
  }
};
  1. 接口请求优化
// 请求去重和缓存
class APIManager {
  constructor() {
    this.cache = new Map();
    this.pendingRequests = new Map();
  }
  
  async request(url, options = {}) {
    const key = `${url}:${JSON.stringify(options)}`;
    
    // 检查缓存
    if (this.cache.has(key)) {
      const cached = this.cache.get(key);
      if (Date.now() - cached.timestamp < 30000) {
        return cached.data;
      }
    }
    
    // 防止重复请求
    if (this.pendingRequests.has(key)) {
      return this.pendingRequests.get(key);
    }
    
    const request = fetch(url, options).then(res => res.json());
    this.pendingRequests.set(key, request);
    
    try {
      const data = await request;
      this.cache.set(key, { data, timestamp: Date.now() });
      return data;
    } finally {
      this.pendingRequests.delete(key);
    }
  }
}

优化结果:

  • 列表渲染性能提升90%,支持10万条数据流畅滚动
  • 内存使用稳定,无泄漏现象
  • 接口请求减少70%,服务器响应时间提升50%

总结

老旧项目性能优化是一个系统工程,需要从分析、优化、监控三个维度进行全面考虑:

关键成功因素

  1. 全面的性能分析 - 使用合适的工具找出真正的性能瓶颈
  2. 分层次的优化策略 - 从构建到运行时的全链路优化
  3. 持续的监控体系 - 确保优化效果的可持续性
  4. 团队协作和知识传承 - 让优化成果在团队中延续

面试要点

  • 体现技术深度和实战经验
  • 强调业务价值和用户体验改善
  • 展示系统思维和持续改进能力
  • 准备具体的数据和案例支撑

性能优化不仅是技术问题,更是工程问题。在面试中展现你的全面思考能力,会让你在众多候选人中脱颖而出。

Logo

有“AI”的1024 = 2048,欢迎大家加入2048 AI社区

更多推荐