IntersectionObserver 是现代浏览器中实现懒加载最轻量可靠的方式,不依赖 scroll 事件,通过 rootMargin 提前加载防闪动,需复用实例、及时 unobserve,并注意兼容性与 DOM 状态。

什么是 IntersectionObserver 懒加载
现代浏览器中,IntersectionObserver 是实现图片/组件懒加载最轻量、最可靠的方式。它不依赖 scroll 事件监听,避免频繁重排重绘和性能抖动,比手动计算 getBoundingClientRect() + scroll 更稳定。
典型适用场景:长列表中的图片、折叠面板里的富媒体内容、无限滚动的卡片流。
- 兼容性需注意:
IE完全不支持,Edge 15+、Chrome 51+、Safari 12.1+均可用;如需兼容旧版 IE,必须降级为scroll + throttle方案 - 初始化时传入的
rootMargin很关键——比如设为"200px"表示元素进入视口前 200px 就触发加载,可避免用户滚动过快时出现“白屏闪动” - 观察器实例应复用,不要为每个图片都新建一个
IntersectionObserver
如何用 IntersectionObserver 加载图片
核心逻辑是:先给 的 src 留空或设为占位图,把真实地址存在 data-src;等进入视口后,再把 data-src 赋给 src,并停止观察该元素。
const lazyImageObserver = new IntersectionObserver((entries) => {
entries.forEach(entry => {
if (entry.isIntersecting) {
const img = entry.target;
const src = img.dataset.src;
if (src) {
img.src = src;
img.removeAttribute('data-src');
lazyImageObserver.unobserve(img); // 关键:及时停止观察
}
}
});
}, {
rootMargin: '200px' // 提前加载,防滚动卡顿
});
document.querySelectorAll('img[data-src]').forEach(img => {
lazyImageObserver.observe(img);
});
-
img.removeAttribute('data-src')不仅是清理,也防止重复触发加载(某些情况下isIntersecting可能短暂为false后又变true) - 如果图片加载失败,建议加
onerror回退逻辑,例如显示默认占位图或记录错误 - 服务端开启
HTTP/2或使用srcset+sizes配合懒加载,能进一步减少首屏带宽消耗
懒加载真能优化性能?看这三点
能,但效果取决于使用方式和页面结构。不是所有场景都值得加懒加载,盲目使用反而引入额外 JS 开销和复杂度。
云模块_YunMOK网站管理系统采用PHP+MYSQL为编程语言,搭载自主研发的模块化引擎驱动技术,实现可视化拖拽无技术创建并管理网站!如你所想,无限可能,支持创建任何网站:企业、商城、O2O、门户、论坛、人才等一块儿搞定!永久免费授权,包括商业用途; 默认内置三套免费模板。PC网站+手机网站+适配微信+文章管理+产品管理+SEO优化+组件扩展+NEW Login界面.....目测已经遥遥领先..
立即学习“Java免费学习笔记(深入)”;
- 首屏时间(FCP/LCP)明显下降:尤其当页面含大量高清图但用户只看前几屏时,跳过后续图片解析和解码,LCP 可提升 30%+
-
内存占用降低:未加载的
不会触发解码和纹理上传,对低端设备或 WebView 场景很关键 -
但要注意副作用:如果懒加载逻辑本身没做节流或 observer 创建过多,JS 执行时间可能反超收益;另外,SEO 友好性依赖是否在服务端渲染时已输出真实
src(否则爬虫看不到图片)
常见错误:为什么图片反复加载或根本不触发
这类问题基本都出在观察器配置或 DOM 状态上,不是代码逻辑 bug。
-
root默认是浏览器视口,但如果父容器设置了overflow: hidden且有滚动,而你又没显式传{ root: container },那永远触发不了回调 -
img元素初始height为0(比如没设宽高、父容器未渲染完成),会导致IntersectionObserver认为它“不可见”,即使位置正确也不触发 - 动态插入的图片(如 AJAX 渲染后追加),必须在插入 DOM 后再调用
observe();直接批量querySelectorAll会漏掉后来的节点 - 某些 CSS 动画或
transform会让元素脱离文档流,影响isIntersecting判断,可临时加will-change: transform或改用opacity动画规避










