ZACSOKACH
(Updated: 2025年10月3日 )

前端性能优化实战:那些年我踩过的坑

#性能优化 #前端开发 #React #JavaScript

上周,我的老板拿着手机皱着眉头问我:“为什么我们的网站加载这么慢?“那一刻,我意识到是时候认真对待性能优化了。经过一个月的折腾,我把网站加载时间从8秒降到了2秒。今天想和大家分享一些实战经验,特别是那些我曾经踩过的坑。

先别急着优化,先搞清楚问题在哪

用数据说话,别凭感觉

刚开始我凭感觉优化了一堆东西,结果发现效果微乎其微。后来才知道,要先搞清楚瓶颈在哪里。

Google 的 Core Web Vitals 是个好东西,但别被这些指标吓到:

  • LCP (最大内容绘制):简单说就是用户看到主要内容的时间,目标是2.5秒内
  • FID (首次输入延迟):用户第一次点击页面到页面响应的时间,100毫秒内比较理想
  • CLS (累积布局偏移):页面元素会不会乱跳动,数值越小越好

我用了 Lighthouse 和 Chrome DevTools 的 Performance 面板,发现主要问题是图片太大和JavaScript太多。

资源优化:图片是头号杀手

我的图片优化血泪史

我之前直接上传了4MB的原图,结果加载慢得要死。后来学乖了:

// 我现在用的懒加载组件
const LazyImage = ({ src, alt }) => {
  const [isLoaded, setIsLoaded] = useState(false);
  const [isInView, setIsInView] = useState(false);
  const imgRef = useRef();

  useEffect(() => {
    const observer = new IntersectionObserver(
      ([entry]) => {
        if (entry.isIntersecting) {
          setIsInView(true);
          observer.disconnect();
        }
      },
      { threshold: 0.1 }
    );

    if (imgRef.current) {
      observer.observe(imgRef.current);
    }

    return () => observer.disconnect();
  }, []);

  return (
    <div ref={imgRef} className="lazy-image-container">
      {isInView && (
        <img
          src={src}
          alt={alt}
          onLoad={() => setIsLoaded(true)}
          style={{ opacity: isLoaded ? 1 : 0 }}
        />
      )}
    </div>
  );
};

这个组件帮我减少了60%的初始加载时间。还有,我现在都用 WebP 格式,体积比 JPEG 小30%左右。

代码分割:别让用户下载用不到的代码

我犯过的错误

之前我把整个管理后台的代码都打包进了主文件,结果普通用户也要下载几MB的管理代码。后来我学会了这样拆分:

// 路由级别的代码分割
const HomePage = lazy(() => import('./pages/HomePage'));
const AboutPage = lazy(() => import('./pages/AboutPage'));

// 组件级别的动态导入
const HeavyComponent = lazy(() => 
  import('./components/HeavyComponent').then(module => ({
    default: module.HeavyComponent
  }))
);

// 条件加载管理面板
const AdminPanel = lazy(() => 
  import('./components/AdminPanel').then(module => ({
    default: module.AdminPanel
  }))
);

function App() {
  const [isAdmin, setIsAdmin] = useState(false);
  
  return (
    <div>
      <Suspense fallback={<div>Loading...</div>}>
        <HomePage />
        {isAdmin && (
          <Suspense fallback={<div>Loading admin panel...</div>}>
            <AdminPanel />
          </Suspense>
        )}
      </Suspense>
    </div>
  );
}

这样拆分后,普通用户访问时只需要下载必要的代码,管理后台的代码只有在管理员登录时才会加载。

缓存策略:别让用户重复下载

Service Worker 救了我

我之前有个问题:用户每次访问都要重新下载所有资源。后来我加上了 Service Worker:

// 我的缓存策略
const CACHE_NAME = 'my-app-cache-v1';
const urlsToCache = [
  '/',
  '/static/js/main.js',
  '/static/css/main.css',
  '/static/images/logo.png'
];

self.addEventListener('install', event => {
  event.waitUntil(
    caches.open(CACHE_NAME)
      .then(cache => cache.addAll(urlsToCache))
  );
});

self.addEventListener('fetch', event => {
  event.respondWith(
    caches.match(event.request)
      .then(response => {
        // 缓存命中,直接返回
        if (response) {
          return response;
        }
        
        // 没命中,去网络请求
        return fetch(event.request).then(response => {
          // 检查响应是否有效
          if (!response || response.status !== 200 || response.type !== 'basic') {
            return response;
          }
          
          // 克隆响应,因为响应流只能用一次
          const responseToCache = response.clone();
          
          caches.open(CACHE_NAME)
            .then(cache => {
              cache.put(event.request, responseToCache);
            });
          
          return response;
        });
      })
  );
});

这个简单的缓存策略让我的重复访问速度提升了80%。

React 优化:别让组件瞎渲染

我踩过的 React 坑

我曾经有个列表页面,每次点击按钮整个列表都会重新渲染,性能差得要死。后来学会了这些技巧:

// 使用 React.memo 避免不必要的重渲染
const ExpensiveComponent = React.memo(({ data, onUpdate }) => {
  // 组件逻辑
}, (prevProps, nextProps) => {
  // 自定义比较函数,只有ID变化时才重新渲染
  return prevProps.data.id === nextProps.data.id;
});

// 使用 useMemo 缓存计算结果
const ExpensiveCalculation = ({ items }) => {
  const sortedItems = useMemo(() => {
    console.log('Sorting items...');
    return items.sort((a, b) => a.value - b.value);
  }, [items]); // 只有items变化时才重新排序

  return <div>{sortedItems.map(item => <div key={item.id}>{item.value}</div>)}</div>;
};

// 使用 useCallback 缓存函数
const ParentComponent = ({ items }) => {
  const [count, setCount] = useState(0);
  
  const handleItemClick = useCallback((item) => {
    console.log('Item clicked:', item);
  }, []); // 空依赖数组表示函数永远不会改变

  return (
    <div>
      <button onClick={() => setCount(count + 1)}>Count: {count}</button>
      <ItemList items={items} onItemClick={handleItemClick} />
    </div>
  );
};

这些优化让我的列表页面响应速度提升了3倍。

网络优化:让资源传输更快

HTTP/2 真的很有用

我之前还在用 HTTP/1.1,后来升级到 HTTP/2,发现多路复用真的很有用,可以同时加载多个资源。

还有资源预加载,我现在都会预加载关键资源:

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

<!-- 预取可能需要的资源 -->
<link rel="prefetch" href="/next-page.js" as="script">
<link rel="prefetch" href="/next-page.css" as="style">

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

这些简单的标签让我的页面加载速度又提升了20%。

图片优化:响应式图片是王道

我的图片优化经验

我现在都用响应式图片,根据设备尺寸加载不同大小的图片:

// 响应式图片组件
const ResponsiveImage = ({ src, alt, sizes }) => {
  return (
    <picture>
      <source
        media="(min-width: 1024px)"
        srcSet={`${src}?w=1200&f=webp 1x, ${src}?w=2400&f=webp 2x`}
        type="image/webp"
      />
      <source
        media="(min-width: 768px)"
        srcSet={`${src}?w=800&f=webp 1x, ${src}?w=1600&f=webp 2x`}
        type="image/webp"
      />
      <img
        src={`${src}?w=400&f=auto`}
        alt={alt}
        sizes={sizes}
        loading="lazy"
        decoding="async"
      />
    </picture>
  );
};

这样移动设备不会下载桌面端的大图,节省了大量带宽。

性能监控:别盲目优化

我的监控工具箱

我现在用这些工具监控性能:

  • Lighthouse: 定期做性能审计
  • WebPageTest: 分析不同地区的加载情况
  • Chrome DevTools: 本地调试性能问题
  • Real User Monitoring: 收集真实用户数据

我还设置了性能预算,防止代码膨胀:

{
  "performanceBudget": {
    "totalSize": "250KB",
    "javascriptSize": "100KB",
    "cssSize": "50KB",
    "imageSize": "100KB",
    "fontSize": "50KB",
    "maxRequestCount": 20
  }
}

总结:性能优化的心得

经过这一个月的折腾,我总结了几点心得:

  1. 先测量,再优化:别凭感觉,用数据说话
  2. 图片是头号杀手:优化图片往往效果最明显
  3. 懒加载是神器:别让用户下载用不到的东西
  4. 缓存很重要:合理的缓存策略能大幅提升体验
  5. 持续监控:性能优化是个持续的过程

最重要的是,性能优化不是一次性的工作,而是需要持续关注的过程。现在我每周都会检查性能数据,确保网站保持良好的状态。

希望我的经验能帮到大家,别再像我一样踩同样的坑了!