IntersectionObserver 是浏览器原生异步回调机制,用于监听元素进入/离开视口,性能优于 scroll + getBoundingClientRect();它不触发强制同步布局,支持 root、threshold、rootMargin 配置,需处理加载失败、重复绑定、内存泄漏等问题,并在不支持时降级为节流 scroll + requestIdleCallback。
它不是轮询检测,而是浏览器原生的异步回调机制,性能远好于 scroll + getBoundingClientRect()。尤其在长列表或滚动频繁的页面里,后者容易触发强制同步布局(Layout Thrashing),造成卡顿。
关键点:它只在元素进入/离开视口时才通知,且不阻塞主线程;而 getBoundingClientRect() 每次调用都可能触发重排。
核心是创建一个 IntersectionObserver 实例,传入回调函数和配置项,再用 observe() 绑定目标 元素。
root:默认为浏览器视口;设为某个容器元素时,就以该容器为“视口”边界threshold:0~1 的数组,表示目标元素多少比例进入 root 时触发回调(如 [0, 0.25, 0.5, 0.75, 1])rootMargin:类似 CSS margin,可提前触发(如 "100px" 表示元素距离视口还有 100px 就开始加载)const imgObserver = new IntersectionObserver(
(entries) => {
entries.forEach(entry => {
if (entry.isIntersecting) {
const img = entry.target;
img.src = img.dataset.src;
img.classList.remove('lazy');
imgObserver.unobserve(img); // 加载完就停止监听
}
});
},
{
rootMargin: '100px', // 提前加载,防滚动过快出现空白
threshold: 0.1
}
);
document.querySelectorAll('img.lazy').forEach(img => {
imgObserver.observe(img);
});
懒加载不是加个 src 就完事——网络异常、路径错误、CORS 都会让 img 显示为空白,且 IntersectionObserver 不会二次触发。
立即学习“Java免费学习笔记(深入)”;
img.onerror,降级显示占位图或提示文字observe()(比如组件重复渲染时没清理旧 observer)unobserve() 要及时调用;否则即使图片已加载,observer 仍持有引用,影响内存回收src,要确保它为空或为占位图,否则浏览器会提前发起请求,失去懒加载意义如果必须支持 Safari 12 或更早版本,不能直接用 IntersectionObserver。可用 scroll + requestIdleCallback 组合做轻量降级,但要注意节流。
简单判断:
if ('IntersectionObserver' in window) {
// 使用 IntersectionObserver
} else {
// 降级:监听 scroll,用 getBoundingClientRect + throttle
// 注意:避免在回调中直接操作 DOM,优先用 requestIdleCallback 延后执行
}
真正麻烦的不是写降级逻辑,而是混合使用时容易漏掉 unobserve 或重复绑定;建议项目里统一抽象成一个 useLazyImage Hook 或工具函数,把判断、绑定、错误处理、清理全包进去。