CodeWalk

Lazy Loading 懒加载的实现方式与 Intersection Observer

作者:编译有声 · 2026-05-30 12:55

请解释图片/组件的懒加载(Lazy Loading)实现原理,对比传统 scroll 监听与 Intersection Observer API 的优劣,以及 React.lazy 的工作原理。

回答

编译有声

图片懒加载(Imgage Lazy Loading)

传统 scroll 监听

window.addEventListener('scroll', () => {
  const images = document.querySelectorAll('img[data-src]');
  images.forEach(img => {
    if (img.getBoundingClientRect().top < window.innerHeight) {
      img.src = img.dataset.src;
      img.removeAttribute('data-src');
    }
  });
});
  • 缺点:scroll 事件频繁触发(需节流),性能开销大,滚动监听阻塞主线程

Intersection Observer(推荐)

const observer = new IntersectionObserver((entries) => {
  entries.forEach(entry => {
    if (entry.isIntersecting) {
      const img = entry.target;
      img.src = img.dataset.src;
      observer.unobserve(img);
    }
  });
}, {
  rootMargin: '200px', // 提前 200px 加载
  threshold: 0.01,
});

document.querySelectorAll('img[data-src]').forEach(img => observer.observe(img));
  • 异步执行,不阻塞主线程
  • 浏览器原生实现,性能优于 scroll 监听
  • 支持 rootMargin 提前加载

原生属性<img loading="lazy">(Chrome 76+ 支持)

组件懒加载(React.lazy)

const HeavyComponent = React.lazy(() => import('./HeavyComponent'));

function App() {
  return (
    <Suspense fallback={<Spinner />}>
      <HeavyComponent />
    </Suspense>
  );
}
  • 底层使用 Webpack 的动态 import() 实现代码分割
  • 结合路由懒加载(React Router 的 lazy)实现页面级按需加载