Skip to content

前端性能优化面试题集

前端性能优化策略与高频面试题 | 更新时间:2025-02

目录

A. 面试宝典

基础题

1. 性能优化概述

┌─────────────────────────────────────────────────────────────┐
│                    性能优化全景                              │
├─────────────────────────────────────────────────────────────┤
│                                                              │
│  网络层面:                                                  │
│  ├── DNS 预解析                                             │
│  ├── HTTP/2 多路复用                                        │
│  ├── CDN 加速                                               │
│  ├── 资源压缩(Gzip/Brotli)                                │
│  ├── 减少请求数量                                           │
│  └── 缓存策略                                               │
│                                                              │
│  资源层面:                                                  │
│  ├── 代码分割                                               │
│  ├── 懒加载                                                 │
│  ├── 预加载(preload/prefetch)                             │
│  ├── 图片优化                                               │
│  └── Tree Shaking                                          │
│                                                              │
│  渲染层面:                                                  │
│  ├── 关键渲染路径优化                                       │
│  ├── 减少重排重绘                                           │
│  ├── GPU 加速                                               │
│  ├── 虚拟列表                                               │
│  └── 骨架屏                                                 │
│                                                              │
│  代码层面:                                                  │
│  ├── 防抖节流                                               │
│  ├── 缓存计算结果                                           │
│  ├── Web Worker                                             │
│  └── 内存管理                                               │
│                                                              │
└─────────────────────────────────────────────────────────────┘

2. 资源加载优化

预加载策略:

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

<!-- 预连接(DNS + TCP + TLS) -->
<link rel="preconnect" href="https://api.example.com">

<!-- 预加载(当前页面需要的关键资源) -->
<link rel="preload" href="critical.css" as="style">
<link rel="preload" href="font.woff2" as="font" type="font/woff2" crossorigin>
<link rel="preload" href="hero.jpg" as="image">

<!-- 预获取(下一个页面可能需要的资源) -->
<link rel="prefetch" href="next-page.js">

<!-- 预渲染(下一个页面) -->
<link rel="prerender" href="https://example.com/next-page">

脚本加载优化:

html
<!-- 普通脚本(阻塞) -->
<script src="app.js"></script>

<!-- async:异步加载,下载完立即执行(适合独立脚本如统计) -->
<script async src="analytics.js"></script>

<!-- defer:异步加载,DOM 解析完后按顺序执行 -->
<script defer src="app.js"></script>

<!-- 模块脚本(默认 defer) -->
<script type="module" src="app.js"></script>
┌─────────────────────────────────────────────────────────────┐
│                    脚本加载对比                              │
├─────────────────────────────────────────────────────────────┤
│                                                              │
│  普通脚本:                                                 │
│  HTML: ████████░░░░░░░░████████████████                    │
│  JS:          ████████                                      │
│       解析    下载执行    继续解析                          │
│                                                              │
│  async:                                                    │
│  HTML: ██████████████████░░████████████                    │
│  JS:        ████████                                        │
│       并行下载     立即执行                                  │
│                                                              │
│  defer:                                                    │
│  HTML: ██████████████████████████████                      │
│  JS:        ████████              ████                      │
│       并行下载              DOM后执行                        │
│                                                              │
└─────────────────────────────────────────────────────────────┘

3. 图片优化

┌─────────────────────────────────────────────────────────────┐
│                    图片格式选择                              │
├─────────────────────────────────────────────────────────────┤
│                                                              │
│  格式        特点                    适用场景               │
│  ──────────────────────────────────────────────────────────│
│  JPEG       有损、不透明             照片                   │
│  PNG        无损、透明               图标、UI               │
│  GIF        动图、256色              简单动画               │
│  WebP       体积小、支持透明动画     通用(需兼容)         │
│  AVIF       更小、更高质量           现代浏览器             │
│  SVG        矢量、可缩放             图标、图形             │
│                                                              │
└─────────────────────────────────────────────────────────────┘
html
<!-- 响应式图片 -->
<picture>
  <source srcset="image.avif" type="image/avif">
  <source srcset="image.webp" type="image/webp">
  <img src="image.jpg" alt="description">
</picture>

<!-- 分辨率适配 -->
<img srcset="small.jpg 480w,
             medium.jpg 800w,
             large.jpg 1200w"
     sizes="(max-width: 600px) 480px,
            (max-width: 1000px) 800px,
            1200px"
     src="medium.jpg"
     alt="description">

<!-- 懒加载 -->
<img src="placeholder.jpg"
     data-src="actual.jpg"
     loading="lazy"
     alt="description">
javascript
// 懒加载实现
const observer = new IntersectionObserver((entries) => {
  entries.forEach(entry => {
    if (entry.isIntersecting) {
      const img = entry.target;
      img.src = img.dataset.src;
      img.classList.remove('lazy');
      observer.unobserve(img);
    }
  });
}, { rootMargin: '100px' });

document.querySelectorAll('img.lazy').forEach(img => {
  observer.observe(img);
});

4. 代码分割

Webpack 代码分割:

javascript
// 动态导入
const module = await import('./module.js');

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

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

// Vue 异步组件
const AsyncComponent = defineAsyncComponent(() =>
  import('./AsyncComponent.vue')
);

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

// Webpack 魔法注释
import(
  /* webpackChunkName: "my-chunk" */
  /* webpackPrefetch: true */
  './module.js'
);

Vite 代码分割:

javascript
// vite.config.js
export default {
  build: {
    rollupOptions: {
      output: {
        manualChunks: {
          vendor: ['react', 'react-dom'],
          utils: ['lodash', 'dayjs']
        }
      }
    }
  }
}

5. 缓存策略

┌─────────────────────────────────────────────────────────────┐
│                    缓存策略                                  │
├─────────────────────────────────────────────────────────────┤
│                                                              │
│  强缓存:                                                   │
│  Cache-Control: max-age=31536000, immutable                 │
│  Expires: Wed, 21 Oct 2025 07:28:00 GMT                    │
│                                                              │
│  协商缓存:                                                 │
│  ETag / If-None-Match                                       │
│  Last-Modified / If-Modified-Since                          │
│                                                              │
│  推荐策略:                                                 │
│  ──────────────────────────────────────────────────────────│
│  HTML:       Cache-Control: no-cache                        │
│              ETag: "abc123"                                 │
│                                                              │
│  JS/CSS:     Cache-Control: max-age=31536000, immutable    │
│              (配合文件名 hash:app.a1b2c3.js)              │
│                                                              │
│  图片:       Cache-Control: max-age=86400                   │
│                                                              │
│  API:        Cache-Control: no-store                        │
│                                                              │
└─────────────────────────────────────────────────────────────┘

Service Worker 缓存:

javascript
// sw.js
const CACHE_NAME = 'v1';
const ASSETS = [
  '/',
  '/index.html',
  '/styles.css',
  '/app.js'
];

// 安装
self.addEventListener('install', (e) => {
  e.waitUntil(
    caches.open(CACHE_NAME)
      .then(cache => cache.addAll(ASSETS))
  );
});

// 请求拦截
self.addEventListener('fetch', (e) => {
  e.respondWith(
    caches.match(e.request)
      .then(response => response || fetch(e.request))
  );
});

// 激活(清理旧缓存)
self.addEventListener('activate', (e) => {
  e.waitUntil(
    caches.keys().then(keys =>
      Promise.all(
        keys.filter(key => key !== CACHE_NAME)
            .map(key => caches.delete(key))
      )
    )
  );
});

进阶题

6. 首屏优化

┌─────────────────────────────────────────────────────────────┐
│                    首屏优化策略                              │
├─────────────────────────────────────────────────────────────┤
│                                                              │
│  1. 减少关键资源                                            │
│     - 内联关键 CSS                                          │
│     - 延迟非关键 CSS                                        │
│     - 异步加载 JS                                           │
│                                                              │
│  2. 优化资源加载                                            │
│     - 预加载关键资源                                        │
│     - 预连接第三方域名                                      │
│     - HTTP/2 Server Push                                    │
│                                                              │
│  3. 减少资源体积                                            │
│     - Gzip/Brotli 压缩                                      │
│     - 代码压缩混淆                                          │
│     - Tree Shaking                                          │
│     - 图片压缩                                              │
│                                                              │
│  4. 优化渲染                                                │
│     - 骨架屏                                                │
│     - 服务端渲染(SSR)                                     │
│     - 静态生成(SSG)                                       │
│     - 流式渲染                                              │
│                                                              │
└─────────────────────────────────────────────────────────────┘
html
<!-- 关键 CSS 内联 -->
<head>
  <style>
    /* 首屏关键样式 */
    .hero { ... }
    .nav { ... }
  </style>
  <!-- 异步加载非关键 CSS -->
  <link rel="preload" href="main.css" as="style"
        onload="this.onload=null;this.rel='stylesheet'">
  <noscript><link rel="stylesheet" href="main.css"></noscript>
</head>

骨架屏:

javascript
// Vue 骨架屏
const Skeleton = {
  template: `
    <div class="skeleton">
      <div class="skeleton-header"></div>
      <div class="skeleton-content">
        <div class="skeleton-line"></div>
        <div class="skeleton-line short"></div>
      </div>
    </div>
  `
};

// 使用
<Suspense>
  <template #default>
    <AsyncComponent />
  </template>
  <template #fallback>
    <Skeleton />
  </template>
</Suspense>

7. 长列表优化

虚拟列表原理:

┌─────────────────────────────────────────────────────────────┐
│                    虚拟列表                                  │
├─────────────────────────────────────────────────────────────┤
│                                                              │
│  普通列表:渲染所有 10000 个 DOM 节点                       │
│                                                              │
│  虚拟列表:只渲染可视区域 + 缓冲区的节点                    │
│                                                              │
│  ┌─────────────────────────────────────────┐                │
│  │ 上方占位区(padding/transform)         │                │
│  ├─────────────────────────────────────────┤                │
│  │ 缓冲区(预渲染)                        │                │
│  ├─────────────────────────────────────────┤                │
│  │                                         │                │
│  │           可视区域                      │   ← viewport   │
│  │                                         │                │
│  ├─────────────────────────────────────────┤                │
│  │ 缓冲区(预渲染)                        │                │
│  ├─────────────────────────────────────────┤                │
│  │ 下方占位区(padding/transform)         │                │
│  └─────────────────────────────────────────┘                │
│                                                              │
└─────────────────────────────────────────────────────────────┘
javascript
// 简易虚拟列表实现
function VirtualList({ items, itemHeight, containerHeight }) {
  const [scrollTop, setScrollTop] = useState(0);

  const startIndex = Math.floor(scrollTop / itemHeight);
  const endIndex = Math.min(
    items.length - 1,
    startIndex + Math.ceil(containerHeight / itemHeight) + 1
  );

  const visibleItems = items.slice(startIndex, endIndex + 1);
  const totalHeight = items.length * itemHeight;
  const offsetY = startIndex * itemHeight;

  return (
    <div
      style={{ height: containerHeight, overflow: 'auto' }}
      onScroll={(e) => setScrollTop(e.target.scrollTop)}
    >
      <div style={{ height: totalHeight, position: 'relative' }}>
        <div style={{ transform: `translateY(${offsetY}px)` }}>
          {visibleItems.map((item, i) => (
            <div key={startIndex + i} style={{ height: itemHeight }}>
              {item.content}
            </div>
          ))}
        </div>
      </div>
    </div>
  );
}

8. 运行时性能

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

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

// 使用 requestIdleCallback 处理低优先级任务
function processLargeData(data) {
  const chunks = splitIntoChunks(data, 100);
  let index = 0;

  function processChunk(deadline) {
    while (index < chunks.length && deadline.timeRemaining() > 0) {
      processItem(chunks[index]);
      index++;
    }

    if (index < chunks.length) {
      requestIdleCallback(processChunk);
    }
  }

  requestIdleCallback(processChunk);
}

// Web Worker 处理耗时计算
// main.js
const worker = new Worker('worker.js');
worker.postMessage({ data: largeData });
worker.onmessage = (e) => {
  console.log('Result:', e.data);
};

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

9. React 性能优化

javascript
// React.memo - 避免不必要的重渲染
const MemoComponent = React.memo(function Component({ data }) {
  return <div>{data}</div>;
}, (prevProps, nextProps) => {
  return prevProps.data === nextProps.data;
});

// useMemo - 缓存计算结果
function Component({ items }) {
  const expensiveValue = useMemo(() => {
    return items.reduce((sum, item) => sum + item.value, 0);
  }, [items]);

  return <div>{expensiveValue}</div>;
}

// useCallback - 缓存函数引用
function Parent() {
  const [count, setCount] = useState(0);

  const handleClick = useCallback(() => {
    console.log('clicked');
  }, []);

  return <Child onClick={handleClick} />;
}

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

// 使用 key 优化列表
{items.map(item => (
  <Item key={item.id} data={item} />
))}

// 避免内联对象/函数
// Bad
<Component style={{ color: 'red' }} onClick={() => {}} />

// Good
const style = useMemo(() => ({ color: 'red' }), []);
const handleClick = useCallback(() => {}, []);
<Component style={style} onClick={handleClick} />

避坑指南

误区正确理解
过度优化先测量,针对瓶颈优化
全部使用懒加载关键资源应预加载
缓存时间越长越好需要考虑更新需求
虚拟列表解决一切简单场景用分页更合适

B. 实战文档

性能测量工具

javascript
// Lighthouse (Chrome DevTools)
// - Performance 评分
// - 核心 Web Vitals
// - 优化建议

// Performance API
const timing = performance.timing;
console.log({
  DNS: timing.domainLookupEnd - timing.domainLookupStart,
  TCP: timing.connectEnd - timing.connectStart,
  TTFB: timing.responseStart - timing.requestStart,
  Download: timing.responseEnd - timing.responseStart,
  DOMParse: timing.domInteractive - timing.responseEnd,
  DOMReady: timing.domContentLoadedEventEnd - timing.navigationStart,
  Load: timing.loadEventEnd - timing.navigationStart
});

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

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

// Web Vitals
import { getCLS, getFID, getLCP } from 'web-vitals';
getCLS(console.log);
getFID(console.log);
getLCP(console.log);

Webpack 优化配置

javascript
// webpack.config.js
module.exports = {
  optimization: {
    // 代码分割
    splitChunks: {
      chunks: 'all',
      cacheGroups: {
        vendor: {
          test: /[\\/]node_modules[\\/]/,
          name: 'vendors',
          chunks: 'all'
        }
      }
    },
    // 运行时代码单独打包
    runtimeChunk: 'single',
    // 压缩
    minimize: true,
    minimizer: [
      new TerserPlugin({
        parallel: true,
        terserOptions: {
          compress: { drop_console: true }
        }
      }),
      new CssMinimizerPlugin()
    ]
  },
  // 开启 Tree Shaking
  mode: 'production',
  // 分析插件
  plugins: [
    new BundleAnalyzerPlugin()
  ]
};

基于 VitePress 构建