利用 IntersectionObserver 几行代码简单实现图片懒加载

利用 IntersectionObserver 几行代码简单实现图片懒加载

June 15, 2023
分享 ,

博客之前集成了 Douban 数据的展示功能,不过由于数据一次性加载过多,且都是有图片的数,导致在进入该网页时会使浏览器卡死 3-5 秒,等到图片完全加载完毕后才能正常操作,非常影响用户体验。

我检查了一下,发现卡顿的原因主要还是一次性发起的图片加载请求太多了,查了下资料,似乎下一代的 HTTP2.0 协议好像对这方面做了优化。但是常规做法还是使用懒加载来处理,也就是滚到图片位置才进行图片请求。

不过我不太想使用第三方的库来处理这个问题,其他库用起来麻烦,而且很臃肿,我其实只要简单的实现这个功能就好了。之前在移植 Smart 主题的 TOC 功能到二〇一九上时发现他使用了 IntersectionObserver 这个函数来处理,便查了一下这个函数的介绍:

Intersection Observer API 提供了一种异步检测目标元素与祖先元素或 viewport 相交情况变化的方法。

这个函数多用于以下几种情况

  • 图片懒加载 —— 当图片滚动到可见时才进行加载
  • 内容无限滚动 —— 也就是用户滚动到接近内容底部时直接加载更多,而无需用户操作翻页,给用户一种网页可以无限滚动的错觉
  • 检测广告的曝光情况 —— 为了计算广告收益,需要知道广告元素的曝光情况
  • 在用户看见某个区域时执行任务或播放动画

后查了一些范例,便参照这个教程的实现了图片的懒加载功能

<div class="lazyload-wrapper ">
	<img src="https://iph.href.lu/150x220?text=Loading&fg=666666&bg=cccccc" data-src="{%= o.douban[i].poster %}" referrer-policy="no-referrer" class="lazyload" alt="{%= o.douban[i].title %}" width="150" height="220">
</div>
处理图片 src
      let images = document.querySelectorAll(".lazyload");
      let observer = new IntersectionObserver(entries => {
        entries.forEach(item => {
          if (item.isIntersecting) {
            item.target.src = item.target.dataset.src; // 开始加载图片,把data-src的值放到src
            observer.unobserve(item.target); // 停止监听已开始加载的图片
          }
        });
      },
        {
          rootMargin: "0px 0px 500px 0px" // 交叉视图前的500像素时,就开始派发事件
        }
      );
      images.forEach(item => observer.observe(item));
js 实现主体
rootMargin 示例

原理其实很简单

  • 先修改你的所有图片,将 img 元素的 src 属性指定为空或者一个展位图,然后添加的一个 data 属性存放图片链接,如 data-src ,添加 lazyload 类用于之后可以获取到图片。
  • 通过之前设置的类名获取所有需要懒加载的图片元素。
  • 创建一个 IntersectionObserver 对象,并传入一个处理函数和 rootMagin
  • 处理函数的主要内容是检测被观测到的对象的 isIntersecting(如果 isIntersecting 为真,则该图片至少已经达到我们设置的 rootMargin 值以内) 判断,如果进入了目标区间内则将 img 的 data-src 设置为 src 属性。
  • 并通过调用 unobserve 停止对这个片检测。

我记得以前实现起来挺复杂的,现在有了个函数只需要简单的几行代码就可以实现,真的方便。

END

🥳
后续:主题修改完上传到服务器后发现还是会出现卡死的情况,通过使用 Chrome 的性能工具发现 pangu.autoSpacingPage () 阻塞了进程...。
🥳
后续 2:根据「剑公子 」的留言,现代浏览器已经原生支持懒加载,只需要在 img 标签中添加属性 loading="lazy" 即可。

通过性能检测工具可以发现 spacingNodespacingNodeByXPath 阻塞了进程,不知道是什么原因。我尝试将调用函数换成 spacingPageBody() 后页面性能恢复正常。可能是 body 之外的某些标签阻塞了函数执行。

加入评论