Skip to content
返回质量保障

懒加载

推迟加载折叠下方内容、图和重组件直到用户需要。

为什么

全量加载的页面加载 3MB 图、200KB 图表库和 80% 用户永不滚到的评论区。懒加载翻转:初始加载仅含可见的(hero、首篇),其余随用户滚动或交互加载。这减初始负载 50-80%,直接改善 LCP 和可交互时间。浏览器原生 loading="lazy" 属性使图懒加载一行改,React.lazy / dynamic import() 使组件懒加载简单。回报即时——页面 1 秒加载替代 3 秒,永不滚到评论的用户永不付加载它们成本。

何时使用

所有折叠下方图、首屏不需要的重组件(图表、编辑器、地图、视频播放器)和路由级代码分割。非折叠上方内容或 LCP 元素。

如何做

1. 用原生 loading="lazy" 属性懒加载图
   - <img src="photo.jpg" loading="lazy" width="800" height="600" />
   - 始终含宽高防布局偏移(CLS)
   - LCP 图设 fetchpriority="high" 优先化
   - 用模糊占位(blur-up)改善感知体验

2. 用 React.lazy 和 Suspense 懒加载组件
   - const Chart = lazy(() => import("./HeavyChart"));
   - 包 <Suspense fallback={<Skeleton />}> 加载时显占位
   - 交互(点击)或滚入视(IntersectionObserver)时加载非挂载时

3. 用动态导入懒加载路由
   - 每路由单独块,仅导航到时加载
   - 空闲时预取下一可能路由(requestIdleCallback)供即时导航
   - 用框架内置代码分割(Next.js dynamic、Vite manualChunks)

4. 用 IntersectionObserver 懒加载数据
   - 评论区滚入视前不取评论
   - const observer = new IntersectionObserver(callback); observer.observe(el);
   - 用户接近底部时加载列表下一页(无限滚动)

5. 测影响
   - 比较懒加载前后 LCP 和总阻塞时间
   - 查 Network 标签: 初始加载应更少更小请求
   - 验证滚动触发懒加载(非反正全加载的定时器)

示例

// 懒图: 原生 loading="lazy"
<img src="hero.jpg" fetchpriority="high" width="1200" height="600" alt="Hero" />
<img src="article-1.jpg" loading="lazy" width="800" height="500" alt="Article 1" />
<img src="article-2.jpg" loading="lazy" width="800" height="500" alt="Article 2" />

// 懒组件: 仅节可见时加载图表
import { lazy, Suspense, useState, useEffect, useRef } from "react";
const HeavyChart = lazy(() => import("./HeavyChart"));

function ChartSection() {
  const [visible, setVisible] = useState(false);
  const ref = useRef<HTMLDivElement>(null);

  useEffect(() => {
    const observer = new IntersectionObserver(([entry]) => {
      if (entry.isIntersecting) setVisible(true);
    }, { rootMargin: "100px" }); // 可见前 100px 开始加载
    if (ref.current) observer.observe(ref.current);
    return () => observer.disconnect();
  }, []);

  return (
    <div ref={ref}>
      {visible && (
        <Suspense fallback={<ChartSkeleton />}>
          <HeavyChart data={data} />
        </Suspense>
      )}
    </div>
  );
}

// 懒路由: 空闲时预取供即时导航
const Settings = lazy(() => import("./routes/Settings"));
// 用户可能导航时预取(如悬停链接)
function SettingsLink() {
  return (
    <Link
      to="/settings"
      onMouseEnter={() => requestIdleCallback(() => import("./routes/Settings"))}
    >
      Settings
    </Link>
  );
}

常见问题

  • ⚠懒加载 LCP 元素(hero 图)延迟最重要内容——LCP 应急切加载带 fetchpriority="high" 非懒。
  • ⚠无占位懒加载(无 Suspense fallback、无骨架)致内容加载时空白闪或布局跳,增 CLS 退化体验。
  • ⚠用定时器触发懒加载(setTimeout 3 秒后全加载)违背目的——用户等 3 秒后全加载,同急切但额外延迟。
  • ⚠无宽高懒加载图致加载时布局偏移——下方内容跳下、CLS 升、用户误点。

最佳实践

  • ✓急切加载 LCP 元素(hero 图、主标题)带 fetchpriority="high" 折叠下方全懒加载——LCP 是用户首见,懒加载它违背目的。
  • ✓始终提供 Suspense fallback 或骨架预留与懒内容同空间——这防布局偏移并给用户内容加载中即时反馈。
  • ✓用带 rootMargin(如 200px)的 IntersectionObserver 在元素全可见前开始加载——使懒加载感即时,内容在用户滚到时已就绪。
  • ✓空闲时预取下一可能路由(链接悬停时 requestIdleCallback)使导航感即时——用户点击时块已在浏览器缓存。