图片懒加载
面试题:图片懒加载怎么实现?滚动监听有什么问题?
懒加载:图片进入(或接近)视口时才加载真实资源,减少首屏请求和带宽,加快首屏。
方案一:原生 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/height或aspect-ratio,预留空间避免累积布局偏移。 - 加载失败兜底:
onerror时替换为默认图。 - 响应式图片:
srcset+sizes按 DPR/视口选择合适尺寸。 - 预加载关键图:首屏关键图不要懒加载,必要时
<link rel="preload">。