Skip to content

前端性能优化面试题

1. 性能优化概述

问题:前端性能优化的目标是什么?有哪些关键指标?

答案

性能优化目标

  • 提高页面加载速度
  • 改善用户体验
  • 提高搜索引擎排名
  • 降低服务器负载

关键性能指标

javascript
// 1. 首次内容绘制(FCP)
// 用户第一次看到内容的时间
// 目标:< 1.8s

// 2. 最大内容绘制(LCP)
// 页面主要内容完全渲染的时间
// 目标:< 2.5s

// 3. 首次输入延迟(FID)
// 用户首次交互的响应时间
// 目标:< 100ms

// 4. 累积布局偏移(CLS)
// 页面布局的稳定性
// 目标:< 0.1

// 5. 首字节时间(TTFB)
// 浏览器接收第一个字节的时间
// 目标:< 600ms

// 6. DOM 内容加载完成(DCL)
// DOM 解析完成的时间

// 7. 完全加载时间(L)
// 所有资源加载完成的时间

性能监控

javascript
// 使用 Performance API
window.addEventListener('load', () => {
  const perfData = performance.getEntriesByType('navigation')[0];
  
  console.log('DNS 查询:', perfData.domainLookupEnd - perfData.domainLookupStart);
  console.log('TCP 连接:', perfData.connectEnd - perfData.connectStart);
  console.log('请求响应:', perfData.responseEnd - perfData.requestStart);
  console.log('DOM 解析:', perfData.domComplete - perfData.domInteractive);
  console.log('页面加载:', perfData.loadEventEnd - perfData.fetchStart);
});

// 使用 PerformanceObserver
const observer = new PerformanceObserver((list) => {
  for (const entry of list.getEntries()) {
    console.log(entry.name, entry.startTime, entry.duration);
  }
});

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

2. 资源加载优化

问题:如何优化资源加载?

答案

代码分割

javascript
// Webpack 代码分割
// 1. 动态导入
import('./module.js').then(module => {
  module.default();
});

// 2. 路由懒加载
const Home = () => import('./views/Home.vue');
const About = () => import('./views/About.vue');

const routes = [
  { path: '/', component: Home },
  { path: '/about', component: About }
];

// 3. 预加载
import(/* webpackPrefetch: true */ './module.js');

// 4. 预获取
import(/* webpackPreload: true */ './module.js');

// 5. SplitChunksPlugin 配置
module.exports = {
  optimization: {
    splitChunks: {
      chunks: 'all',
      minSize: 30000,
      maxSize: 244000,
      minChunks: 1,
      maxAsyncRequests: 5,
      maxInitialRequests: 3,
      automaticNameDelimiter: '~',
      cacheGroups: {
        vendors: {
          test: /[\\/]node_modules[\\/]/,
          priority: -10
        },
        default: {
          minChunks: 2,
          priority: -20,
          reuseExistingChunk: true
        }
      }
    }
  }
};

资源压缩

javascript
// 1. Gzip 压缩
// Webpack 配置
const CompressionPlugin = require('compression-webpack-plugin');

module.exports = {
  plugins: [
    new CompressionPlugin({
      algorithm: 'gzip',
      test: /\.(js|css|html|svg)$/,
      threshold: 10240,
      minRatio: 0.8
    })
  ]
};

// 2. Brotli 压缩
const BrotliPlugin = require('brotli-webpack-plugin');

module.exports = {
  plugins: [
    new BrotliPlugin({
      test: /\.(js|css|html|svg)$/,
      threshold: 10240,
      minRatio: 0.8
    })
  ]
};

// 3. 图片压缩
const ImageMinimizerPlugin = require('image-minimizer-webpack-plugin');

module.exports = {
  plugins: [
    new ImageMinimizerPlugin({
      minimizer: {
        implementation: ImageMinimizerPlugin.imageminGenerate,
        options: {
          plugins: [
            ['gifsicle', { interlaced: true }],
            ['jpegtran', { progressive: true }],
            ['optipng', { optimizationLevel: 5 }],
            ['svgo', { plugins: [{ name: 'removeViewBox', active: false }] }]
          ]
        }
      }
    })
  ]
};

CDN 加速

html
<!-- 使用 CDN 加载第三方库 -->
<script src="https://cdn.jsdelivr.net/npm/vue@3/dist/vue.global.js"></script>
<script src="https://cdn.jsdelivr.net/npm/axios/dist/axios.min.js"></script>

<!-- 使用 CDN 加载图片 -->
<img src="https://cdn.example.com/images/photo.jpg" alt="Photo">

<!-- 使用 CDN 加载字体 -->
<link href="https://cdn.example.com/fonts/font.woff2" rel="preload" as="font" type="font/woff2" crossorigin>

资源预加载

html
<!-- 预加载关键资源 -->
<link rel="preload" href="/styles/main.css" as="style">
<link rel="preload" href="/scripts/main.js" as="script">
<link rel="preload" href="/fonts/main.woff2" as="font" type="font/woff2" crossorigin>

<!-- 预获取可能需要的资源 -->
<link rel="prefetch" href="/page2.html">
<link rel="prefetch" href="/images/photo.jpg">

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

<!-- 预连接 -->
<link rel="preconnect" href="https://cdn.example.com">
<link rel="preconnect" href="https://api.example.com">

3. 渲染性能优化

问题:如何优化渲染性能?

答案

减少重排和重绘

javascript
// 1. 批量 DOM 操作
// 不好:多次操作 DOM
for (let i = 0; i < 100; i++) {
  document.body.innerHTML += '<div>Item ' + i + '</div>';
}

// 好:使用 DocumentFragment
const fragment = document.createDocumentFragment();
for (let i = 0; i < 100; i++) {
  const div = document.createElement('div');
  div.textContent = 'Item ' + i;
  fragment.appendChild(div);
}
document.body.appendChild(fragment);

// 2. 使用虚拟 DOM
// React、Vue 等框架自动优化
const items = Array.from({ length: 100 }, (_, i) => ({ id: i, text: 'Item ' + i }));

// 3. 使用 CSS transform 代替 top/left
// 不好:触发重排
element.style.left = '100px';
element.style.top = '100px';

// 好:只触发合成
element.style.transform = 'translate(100px, 100px)';

// 4. 使用 opacity 代替 visibility
// 不好:触发重排
element.style.visibility = 'hidden';

// 好:只触发重绘
element.style.opacity = '0';

// 5. 批量读取和写入
// 不好:交错读写
const width1 = element.offsetWidth;
element.style.width = width1 + 'px';
const height1 = element.offsetHeight;
element.style.height = height1 + 'px';

// 好:批量读取后批量写入
const width = element.offsetWidth;
const height = element.offsetHeight;
element.style.width = width + 'px';
element.style.height = height + 'px';

虚拟滚动

javascript
// 虚拟列表实现
class VirtualList {
  constructor(container, itemHeight, renderItem) {
    this.container = container;
    this.itemHeight = itemHeight;
    this.renderItem = renderItem;
    this.visibleItems = [];
    this.startIndex = 0;
    this.endIndex = 0;
    
    this.init();
  }
  
  init() {
    this.container.addEventListener('scroll', () => this.onScroll());
    this.render();
  }
  
  onScroll() {
    const scrollTop = this.container.scrollTop;
    const containerHeight = this.container.clientHeight;
    
    this.startIndex = Math.floor(scrollTop / this.itemHeight);
    this.endIndex = Math.min(
      this.startIndex + Math.ceil(containerHeight / this.itemHeight),
      this.items.length - 1
    );
    
    this.render();
  }
  
  render() {
    const fragment = document.createDocumentFragment();
    
    for (let i = this.startIndex; i <= this.endIndex; i++) {
      const item = this.renderItem(this.items[i], i);
      item.style.position = 'absolute';
      item.style.top = (i * this.itemHeight) + 'px';
      fragment.appendChild(item);
    }
    
    this.container.innerHTML = '';
    this.container.appendChild(fragment);
  }
}

// 使用
const list = new VirtualList(
  document.getElementById('list-container'),
  50,
  (item, index) => {
    const div = document.createElement('div');
    div.textContent = item.text;
    return div;
  }
);

防抖和节流

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

// 使用
window.addEventListener('resize', debounce(() => {
  console.log('Resize event');
}, 300));

// 节流
function throttle(func, delay) {
  let lastCall = 0;
  return function(...args) {
    const now = Date.now();
    if (now - lastCall >= delay) {
      lastCall = now;
      func.apply(this, args);
    }
  };
}

// 使用
window.addEventListener('scroll', throttle(() => {
  console.log('Scroll event');
}, 100));

// requestAnimationFrame 节流
function rafThrottle(func) {
  let ticking = false;
  return function(...args) {
    if (!ticking) {
      ticking = true;
      requestAnimationFrame(() => {
        func.apply(this, args);
        ticking = false;
      });
    }
  };
}

// 使用
window.addEventListener('scroll', rafThrottle(() => {
  console.log('Scroll event');
}));

4. 内存优化

问题:如何优化内存使用?

答案

避免内存泄漏

javascript
// 1. 及时清理事件监听器
// 不好:忘记移除监听器
function init() {
  window.addEventListener('resize', handleResize);
}

// 好:在组件销毁时移除
function init() {
  const handleResize = () => {
    // 处理调整大小
  };
  
  window.addEventListener('resize', handleResize);
  
  return () => {
    window.removeEventListener('resize', handleResize);
  };
}

const cleanup = init();
// 不需要时调用 cleanup();

// 2. 避免闭包中的大对象
// 不好:闭包引用大对象
function createHandler(largeData) {
  return function() {
    console.log(largeData.length);
  };
}

// 好:只引用需要的数据
function createHandler(dataLength) {
  return function() {
    console.log(dataLength);
  };
}

// 3. 及时清理定时器
// 不好:忘记清理定时器
function startTimer() {
  setInterval(() => {
    console.log('Tick');
  }, 1000);
}

// 好:保存定时器 ID,可以清理
function startTimer() {
  const timerId = setInterval(() => {
    console.log('Tick');
  }, 1000);
  
  return () => clearInterval(timerId);
}

const stopTimer = startTimer();
stopTimer();

// 4. 使用 WeakMap 和 WeakSet
// WeakMap 不会阻止垃圾回收
const cache = new WeakMap();

function processData(obj) {
  if (cache.has(obj)) {
    return cache.get(obj);
  }
  
  const result = expensiveOperation(obj);
  cache.set(obj, result);
  return result;
}

对象池

javascript
// 对象池实现
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(createFn());
    }
  }
  
  acquire() {
    return this.pool.pop() || this.createFn();
  }
  
  release(obj) {
    this.resetFn(obj);
    this.pool.push(obj);
  }
}

// 使用
const pool = new ObjectPool(
  () => ({ x: 0, y: 0 }),
  (obj) => { obj.x = 0; obj.y = 0; }
);

function processItems(items) {
  const results = [];
  
  for (const item of items) {
    const obj = pool.acquire();
    obj.x = item.x;
    obj.y = item.y;
    
    const result = process(obj);
    results.push(result);
    
    pool.release(obj);
  }
  
  return results;
}

5. 网络优化

问题:如何优化网络请求?

答案

HTTP 缓存

javascript
// 1. 设置缓存头
// 服务端
app.use((req, res, next) => {
  res.setHeader('Cache-Control', 'public, max-age=3600');
  res.setHeader('ETag', '"abc123"');
  next();
});

// 2. 使用 Service Worker 缓存
// sw.js
self.addEventListener('install', (event) => {
  event.waitUntil(
    caches.open('v1').then((cache) => {
      return cache.addAll([
        '/',
        '/styles/main.css',
        '/scripts/main.js'
      ]);
    })
  );
});

self.addEventListener('fetch', (event) => {
  event.respondWith(
    caches.match(event.request).then((response) => {
      return response || fetch(event.request);
    })
  );
});

// 3. 使用 IndexedDB 缓存
const request = indexedDB.open('cache', 1);

request.onupgradeneeded = (event) => {
  const db = event.target.result;
  const store = db.createObjectStore('data', { keyPath: 'id' });
  store.createIndex('url', 'url', { unique: true });
};

request.onsuccess = (event) => {
  const db = event.target.result;
  
  // 缓存数据
  const transaction = db.transaction(['data'], 'readwrite');
  const store = transaction.objectStore('data');
  store.put({ id: 1, url: '/api/data', data: {...} });
  
  // 读取缓存
  const getRequest = store.get(1);
  getRequest.onsuccess = (e) => {
    console.log(e.target.result);
  };
};

请求优化

javascript
// 1. 合并请求
// 不好:多次请求
const requests = [
  fetch('/api/user/1'),
  fetch('/api/user/2'),
  fetch('/api/user/3')
];

// 好:批量请求
const response = await fetch('/api/users?ids=1,2,3');
const users = await response.json();

// 2. 使用 GraphQL
const query = `
  query {
    user1: user(id: 1) { name email }
    user2: user(id: 2) { name email }
    user3: user(id: 3) { name email }
  }
`;

const response = await fetch('/graphql', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({ query })
});

// 3. 使用 HTTP/2 多路复用
// 自动支持,无需额外配置

// 4. 使用 WebSocket
const ws = new WebSocket('ws://api.example.com');

ws.onmessage = (event) => {
  const data = JSON.parse(event.data);
  console.log('Received:', data);
};

ws.send(JSON.stringify({ type: 'subscribe', channel: 'updates' }));

数据压缩

javascript
// 1. 请求压缩
const response = await fetch('/api/data', {
  headers: {
    'Accept-Encoding': 'gzip, deflate, br'
  }
});

// 2. 响应压缩
// 服务端配置
app.use(compression({
  filter: (req, res) => {
    if (req.headers['x-no-compression']) {
      return false;
    }
    return compression.filter(req, res);
  },
  threshold: 1024,
  level: 6
}));

// 3. 数据格式优化
// 使用 Protocol Buffers 代替 JSON
// 使用 MessagePack 代替 JSON

6. 图片优化

问题:如何优化图片加载?

答案

图片格式选择

html
<!-- 1. 使用 WebP 格式 -->
<picture>
  <source srcset="image.webp" type="image/webp">
  <source srcset="image.jpg" type="image/jpeg">
  <img src="image.jpg" alt="Image">
</picture>

<!-- 2. 响应式图片 -->
<img 
  src="image-small.jpg"
  srcset="image-small.jpg 500w, image-medium.jpg 1000w, image-large.jpg 1500w"
  sizes="(max-width: 500px) 500px, (max-width: 1000px) 1000px, 1500px"
  alt="Responsive image"
>

<!-- 3. 懒加载 -->
<img 
  src="placeholder.jpg" 
  data-src="image.jpg" 
  loading="lazy"
  alt="Lazy loaded image"
>

<!-- 4. 预加载关键图片 -->
<link rel="preload" href="hero-image.jpg" as="image">

图片压缩

javascript
// 使用 sharp 压缩图片
const sharp = require('sharp');

async function compressImage(inputPath, outputPath) {
  await sharp(inputPath)
    .resize(800, 600, {
      fit: 'inside',
      withoutEnlargement: true
    })
    .jpeg({
      quality: 80,
      progressive: true
    })
    .toFile(outputPath);
}

// 使用 imagemin 压缩图片
const imagemin = require('imagemin');
const imageminJpegtran = require('imagemin-jpegtran');
const imageminPngquant = require('imagemin-pngquant');

await imagemin(['images/*.{jpg,png}'], {
  destination: 'build/images',
  plugins: [
    imageminJpegtran({ progressive: true }),
    imageminPngquant({ quality: [0.6, 0.8] })
  ]
});

CSS Sprites

css
/* 使用 CSS Sprites */
.icon {
  background-image: url('sprite.png');
  background-repeat: no-repeat;
}

.icon-home {
  background-position: 0 0;
  width: 32px;
  height: 32px;
}

.icon-user {
  background-position: -32px 0;
  width: 32px;
  height: 32px;
}

.icon-settings {
  background-position: -64px 0;
  width: 32px;
  height: 32px;
}

7. 字体优化

问题:如何优化字体加载?

答案

字体格式选择

html
<!-- 使用 WOFF2 格式 -->
@font-face {
  font-family: 'CustomFont';
  src: url('font.woff2') format('woff2');
  font-weight: normal;
  font-style: normal;
  font-display: swap;
}

<!-- 字体回退 -->
@font-face {
  font-family: 'CustomFont';
  src: url('font.woff2') format('woff2'),
       url('font.woff') format('woff'),
       url('font.ttf') format('truetype');
  font-weight: normal;
  font-style: normal;
}

字体加载策略

css
/* font-display 属性 */
@font-face {
  font-family: 'CustomFont';
  src: url('font.woff2') format('woff2');
  font-display: auto;      /* 浏览器默认 */
  font-display: block;     /* 短暂隐藏文本 */
  font-display: swap;      /* 立即使用后备字体 */
  font-display: fallback;  /* 短暂使用后备字体 */
  font-display: optional;  /* 极短暂使用后备字体 */
}

字体预加载

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

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

字体子集化

javascript
// 使用 fonttools 生成字体子集
const { Font } = require('fonteditor-core');

async function createFontSubset() {
  const font = await Font.create({
    type: 'ttf',
    filename: 'font.ttf'
  });
  
  // 只保留需要的字符
  const subset = font.subset({
    text: 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'
  });
  
  // 导出子集字体
  await subset.write({
    type: 'woff2',
    filename: 'font-subset.woff2'
  });
}

8. 构建优化

问题:如何优化构建过程?

答案

Webpack 优化

javascript
module.exports = {
  // 1. 模式配置
  mode: 'production',
  
  // 2. 代码分割
  optimization: {
    splitChunks: {
      chunks: 'all',
      minSize: 30000,
      maxSize: 244000
    },
    runtimeChunk: 'single'
  },
  
  // 3. Tree Shaking
  optimization: {
    usedExports: true,
    sideEffects: false
  },
  
  // 4. 压缩
  optimization: {
    minimize: true,
    minimizer: [
      new TerserPlugin({
        terserOptions: {
          compress: {
            drop_console: true,
            drop_debugger: true
          }
        }
      })
    ]
  },
  
  // 5. 缓存
  cache: {
    type: 'filesystem',
    buildDependencies: {
      config: [__filename]
    }
  },
  
  // 6. 持久化缓存
  output: {
    filename: '[name].[contenthash:8].js',
    chunkFilename: '[name].[contenthash:8].js'
  },
  
  plugins: [
    // 7. 压缩 CSS
    new MiniCssExtractPlugin({
      filename: '[name].[contenthash:8].css'
    }),
    new CssMinimizerPlugin(),
    
    // 8. 压缩图片
    new ImageMinimizerPlugin({
      minimizer: {
        implementation: ImageMinimizerPlugin.imageminGenerate,
        options: {
          plugins: [
            ['gifsicle', { interlaced: true }],
            ['jpegtran', { progressive: true }],
            ['optipng', { optimizationLevel: 5 }]
          ]
        }
      }
    })
  ]
};

Vite 优化

javascript
// vite.config.js
export default {
  build: {
    // 1. 代码分割
    rollupOptions: {
      output: {
        manualChunks: {
          'vendor': ['vue', 'vue-router', 'axios'],
          'utils': ['lodash']
        }
      }
    },
    
    // 2. 压缩
    minify: 'terser',
    terserOptions: {
      compress: {
        drop_console: true,
        drop_debugger: true
      }
    },
    
    // 3. chunk 大小警告
    chunkSizeWarningLimit: 1000,
    
    // 4. CSS 代码分割
    cssCodeSplit: true
  },
  
  // 5. 依赖预构建
  optimizeDeps: {
    include: ['vue', 'vue-router', 'axios']
  },
  
  plugins: [
    // 6. 压缩图片
    viteImagemin({
      gifsicle: true,
      optipng: true,
      pngquant: true,
      svgo: true,
      jpegtran: true,
      pluginsDefault: [
        ['gifsicle', { interlaced: true }],
        ['jpegtran', { progressive: true }],
        ['optipng', { optimizationLevel: 5 }]
      ]
    })
  ]
};

9. 性能监控和分析

问题:如何监控和分析性能?

答案

性能监控

javascript
// 1. Web Vitals 监控
import { getCLS, getFID, getLCP } from 'web-vitals';

getCLS(console.log);
getFID(console.log);
getLCP(console.log);

// 2. 自定义性能监控
class PerformanceMonitor {
  constructor() {
    this.metrics = {};
    this.init();
  }
  
  init() {
    // 监控页面加载
    window.addEventListener('load', () => {
      this.recordPageLoad();
    });
    
    // 监控资源加载
    const observer = new PerformanceObserver((list) => {
      for (const entry of list.getEntries()) {
        this.recordResource(entry);
      }
    });
    
    observer.observe({ entryTypes: ['resource'] });
    
    // 监控长任务
    const longTaskObserver = new PerformanceObserver((list) => {
      for (const entry of list.getEntries()) {
        this.recordLongTask(entry);
      }
    });
    
    longTaskObserver.observe({ entryTypes: ['longtask'] });
  }
  
  recordPageLoad() {
    const perfData = performance.getEntriesByType('navigation')[0];
    
    this.metrics.pageLoad = {
      dns: perfData.domainLookupEnd - perfData.domainLookupStart,
      tcp: perfData.connectEnd - perfData.connectStart,
      request: perfData.responseEnd - perfData.requestStart,
      dom: perfData.domComplete - perfData.domInteractive,
      load: perfData.loadEventEnd - perfData.fetchStart
    };
  }
  
  recordResource(entry) {
    if (!this.metrics.resources) {
      this.metrics.resources = [];
    }
    
    this.metrics.resources.push({
      name: entry.name,
      duration: entry.duration,
      size: entry.transferSize
    });
  }
  
  recordLongTask(entry) {
    if (!this.metrics.longTasks) {
      this.metrics.longTasks = [];
    }
    
    this.metrics.longTasks.push({
      duration: entry.duration,
      startTime: entry.startTime
    });
  }
  
  report() {
    // 发送性能数据到服务器
    fetch('/api/performance', {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify(this.metrics)
    });
  }
}

// 使用
const monitor = new PerformanceMonitor();
window.addEventListener('beforeunload', () => monitor.report());

性能分析工具

javascript
// 1. Chrome DevTools Performance
// 打开 DevTools -> Performance -> Record

// 2. Lighthouse
// npm install -g lighthouse
// lighthouse https://example.com --view

// 3. WebPageTest
// 访问 https://www.webpagetest.org/

// 4. PageSpeed Insights
// 访问 https://pagespeed.web.dev/

// 5. 使用 Performance API 分析
function analyzePerformance() {
  const perfData = performance.getEntriesByType('navigation')[0];
  
  console.log('DNS:', perfData.domainLookupEnd - perfData.domainLookupStart);
  console.log('TCP:', perfData.connectEnd - perfData.connectStart);
  console.log('TTFB:', perfData.responseStart - perfData.requestStart);
  console.log('Download:', perfData.responseEnd - perfData.responseStart);
  console.log('DOM:', perfData.domComplete - perfData.domInteractive);
  console.log('Load:', perfData.loadEventEnd - perfData.fetchStart);
  
  // 分析资源
  const resources = performance.getEntriesByType('resource');
  const slowResources = resources.filter(r => r.duration > 1000);
  
  console.log('Slow resources:', slowResources);
}

analyzePerformance();

10. 性能优化最佳实践

问题:前端性能优化有哪些最佳实践?

答案

最佳实践清单

javascript
// 1. 资源优化
// - 使用 CDN
// - 启用 Gzip/Brotli 压缩
// - 使用现代图片格式(WebP)
// - 图片懒加载
// - 字体子集化

// 2. 代码优化
// - 代码分割
// - Tree Shaking
// - 压缩代码
// - 移除未使用的代码
// - 使用现代 JavaScript 特性

// 3. 渲染优化
// - 减少重排和重绘
// - 使用虚拟 DOM
// - 虚拟滚动
// - 防抖和节流
// - 使用 CSS transform 和 opacity

// 4. 缓存优化
// - 使用 HTTP 缓存
// - 使用 Service Worker
// - 使用 IndexedDB
// - 使用 LocalStorage

// 5. 网络优化
// - 合并请求
// - 使用 WebSocket
// - 使用 HTTP/2
// - 数据压缩

// 6. 监控优化
// - 监控 Web Vitals
// - 监控错误
// - 监控性能指标
// - 使用 APM 工具

// 7. 构建优化
// - 使用生产模式
// - 代码分割
// - Tree Shaking
// - 压缩资源
// - 使用缓存

// 8. 用户体验优化
// - 骨架屏
// - 加载动画
// - 错误边界
// - 优雅降级

// 示例:骨架屏
function Skeleton() {
  return (
    <div className="skeleton">
      <div className="skeleton-header" />
      <div className="skeleton-body" />
      <div className="skeleton-footer" />
    </div>
  );
}

// 示例:错误边界
class ErrorBoundary extends React.Component {
  constructor(props) {
    super(props);
    this.state = { hasError: false };
  }
  
  static getDerivedStateFromError(error) {
    return { hasError: true };
  }
  
  componentDidCatch(error, errorInfo) {
    console.error('Error:', error, errorInfo);
  }
  
  render() {
    if (this.state.hasError) {
      return <div>Something went wrong.</div>;
    }
    return this.props.children;
  }
}