Skip to main content

图片懒加载

面试题:图片懒加载怎么实现?滚动监听有什么问题?

懒加载:图片进入(或接近)视口时才加载真实资源,减少首屏请求和带宽,加快首屏。

方案一:原生 loading="lazy"(首选)

<img src="real.jpg" loading="lazy" alt="" />

浏览器原生支持,零成本。缺点:可控性差(无法自定义提前距离、占位逻辑),但大部分场景够用。

方案二:IntersectionObserver(推荐自定义实现)

把真实地址放在 data-src,元素进入视口时再赋给 src

<img data-src="real.jpg" src="placeholder.png" class="lazy" />
const io = new IntersectionObserver(
(entries, observer) => {
entries.forEach((entry) => {
if (entry.isIntersecting) {
const img = entry.target;
img.src = img.dataset.src;
observer.unobserve(img); // 加载后停止观察
}
});
},
{ rootMargin: "200px" } // 提前 200px 加载,避免滚到才出现
);

document.querySelectorAll("img.lazy").forEach((img) => io.observe(img));

IntersectionObserver 由浏览器异步判断相交,不阻塞主线程,比监听 scroll 性能好得多。

方案三:scroll 监听(降级 / 老浏览器)

判断 img.getBoundingClientRect().top < window.innerHeight。问题:

  • scroll 高频触发,getBoundingClientRect 会触发强制同步布局(reflow),性能差。
  • 必须配合节流,且体验不如 IO。

进阶优化

  • 渐进式加载:先加载模糊的小图/LQIP(base64 占位),真实图加载完再替换(淡入),避免布局抖动。
  • 防 CLS:给 img 设置 width/heightaspect-ratio,预留空间避免累积布局偏移。
  • 加载失败兜底onerror 时替换为默认图。
  • 响应式图片srcset + sizes 按 DPR/视口选择合适尺寸。
  • 预加载关键图:首屏关键图不要懒加载,必要时 <link rel="preload">