性能优化总览

前端性能优化的常用手段和实践方案

问题

前端性能优化有哪些常用手段?

解答

1. 加载优化

资源压缩与合并

// webpack 配置示例
module.exports = {
  optimization: {
    minimize: true,
    splitChunks: {
      chunks: 'all',
      cacheGroups: {
        vendors: {
          test: /[\\/]node_modules[\\/]/,
          priority: -10,
          name: 'vendors'
        }
      }
    }
  }
}

图片优化

<!-- 响应式图片 -->
<img 
  srcset="small.jpg 300w, medium.jpg 600w, large.jpg 900w"
  sizes="(max-width: 320px) 280px, (max-width: 640px) 580px, 800px"
  src="medium.jpg"
  alt="响应式图片"
/>

<!-- 懒加载 -->
<img src="placeholder.jpg" data-src="real-image.jpg" loading="lazy" />

代码分割与懒加载

// React 懒加载
const LazyComponent = React.lazy(() => import('./HeavyComponent'))

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

// Vue 路由懒加载
const routes = [
  {
    path: '/dashboard',
    component: () => import('./views/Dashboard.vue')
  }
]

2. 渲染优化

避免强制同步布局

// 错误:读写交替,触发多次重排
elements.forEach(el => {
  const width = el.offsetWidth  // 读
  el.style.width = width + 10 + 'px'  // 写
})

// 正确:批量读,批量写
const widths = elements.map(el => el.offsetWidth)  // 批量读
elements.forEach((el, i) => {
  el.style.width = widths[i] + 10 + 'px'  // 批量写
})

使用 requestAnimationFrame

// 动画使用 rAF
function animate() {
  element.style.transform = `translateX(${position}px)`
  position += 1
  
  if (position < 300) {
    requestAnimationFrame(animate)
  }
}
requestAnimationFrame(animate)

虚拟列表

// 简化版虚拟列表
function VirtualList({ items, itemHeight, containerHeight }) {
  const [scrollTop, setScrollTop] = useState(0)
  
  // 计算可见范围
  const startIndex = Math.floor(scrollTop / itemHeight)
  const endIndex = Math.min(
    startIndex + Math.ceil(containerHeight / itemHeight) + 1,
    items.length
  )
  
  // 只渲染可见项
  const visibleItems = items.slice(startIndex, endIndex)
  const offsetY = startIndex * itemHeight
  
  return (
    <div 
      style={{ height: containerHeight, overflow: 'auto' }}
      onScroll={e => setScrollTop(e.target.scrollTop)}
    >
      <div style={{ height: items.length * itemHeight }}>
        <div style={{ transform: `translateY(${offsetY}px)` }}>
          {visibleItems.map((item, i) => (
            <div key={startIndex + i} style={{ height: itemHeight }}>
              {item}
            </div>
          ))}
        </div>
      </div>
    </div>
  )
}

3. 网络优化

缓存策略

// Service Worker 缓存
self.addEventListener('fetch', event => {
  event.respondWith(
    caches.match(event.request).then(cached => {
      // 缓存优先,同时更新缓存
      const fetched = fetch(event.request).then(response => {
        const clone = response.clone()
        caches.open('v1').then(cache => cache.put(event.request, clone))
        return response
      })
      return cached || fetched
    })
  )
})

HTTP 缓存头

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

location ~* \.html$ {
    expires -1;
    add_header Cache-Control "no-cache, must-revalidate";
}

预加载关键资源

<!-- 预加载 -->
<link rel="preload" href="critical.css" as="style" />
<link rel="preload" href="main.js" as="script" />

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

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

4. 代码优化

防抖与节流

// 防抖:停止触发后执行
function debounce(fn, delay) {
  let timer = null
  return function(...args) {
    clearTimeout(timer)
    timer = setTimeout(() => fn.apply(this, args), delay)
  }
}

// 节流:固定间隔执行
function throttle(fn, interval) {
  let lastTime = 0
  return function(...args) {
    const now = Date.now()
    if (now - lastTime >= interval) {
      lastTime = now
      fn.apply(this, args)
    }
  }
}

// 使用
input.addEventListener('input', debounce(search, 300))
window.addEventListener('scroll', throttle(handleScroll, 100))

Web Worker 处理耗时任务

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

// main.js
const worker = new Worker('worker.js')
worker.postMessage(largeData)
worker.onmessage = function(e) {
  console.log('计算结果:', e.data)
}

5. 性能监控

// 使用 Performance API
const timing = performance.timing
const metrics = {
  // DNS 查询
  dns: timing.domainLookupEnd - timing.domainLookupStart,
  // TCP 连接
  tcp: timing.connectEnd - timing.connectStart,
  // 首字节时间
  ttfb: timing.responseStart - timing.requestStart,
  // DOM 解析
  domParse: timing.domInteractive - timing.responseEnd,
  // 页面完全加载
  load: timing.loadEventEnd - timing.navigationStart
}

// 监控 LCP、FID、CLS
new PerformanceObserver(list => {
  for (const entry of list.getEntries()) {
    console.log('LCP:', entry.startTime)
  }
}).observe({ entryTypes: ['largest-contentful-paint'] })

关键点

  • 加载优化:压缩资源、代码分割、懒加载、使用 CDN
  • 渲染优化:减少重排重绘、使用 rAF、虚拟列表处理大数据
  • 缓存策略:合理设置 HTTP 缓存、使用 Service Worker
  • 代码层面:防抖节流、Web Worker、避免内存泄漏
  • 持续监控:使用 Performance API 和 Web Vitals 指标(LCP、FID、CLS)