web学习笔记编程技术学习与分享
首页 / 前端开发 / 正文

用 IntersectionObserver 做懒加载与曝光统计

2026 年 06 月 16 日约 3 分钟阅读 前端开发

早期做懒加载靠监听 scroll 事件,再逐个调用 getBoundingClientRect()。这会强制同步布局,滚动时极易掉帧。IntersectionObserver 把这类判断交给浏览器,回调在空闲时批量触发。

基本用法

const io = new IntersectionObserver((entries) => {
  entries.forEach(e => {
    if (e.isIntersecting) {
      e.target.src = e.target.dataset.src;
      io.unobserve(e.target);          // 加载一次即可
    }
  });
}, { rootMargin: '200px 0px' });

document.querySelectorAll('img[data-src]').forEach(el => io.observe(el));

rootMargin 提前 200px 触发,用户滚到时图片已经就位,不会看到空白跳变。

三个参数的含义

  • root:参照容器,默认是视口;
  • rootMargin:扩大或缩小判定边界,语法同 CSS margin;
  • threshold:交叉比例阈值,0 表示刚露出一点,1 表示完全可见。

曝光统计通常用 threshold: [0, 0.5],在 50% 可见时上报,避免用户快速划过也被计数。

曝光统计的去重

曝光只应上报一次。用一个 WeakSet 记录已上报元素,元素被回收时集合也随之释放:

const seen = new WeakSet();
const io = new IntersectionObserver((entries) => {
  entries.forEach(e => {
    if (e.intersectionRatio >= 0.5 && !seen.has(e.target)) {
      seen.add(e.target);
      report(e.target.dataset.id);
    }
  });
}, { threshold: [0.5] });

兼容与降级

现代浏览器支持良好,但老版本 Safari 需要 polyfill。降级方案是先判断 'IntersectionObserver' in window,不支持则直接加载全部资源——功能可用性优先于性能优化。

不要过度使用

首屏关键图片不要懒加载,否则会拖慢 LCP。判断标准是:这张图是否在用户不滚动的情况下就能看到。能看到,就正常加载。

另外,IntersectionObserver 只告诉你「可见性变化」,不告诉你「是否被遮挡」「是否在后台标签页」。需要更精细的可见性判断时,配合 document.visibilityState 使用。

浏览器性能API