👨‍💻 只是玩玩 | JUST FUN

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

博客之前集成了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之外的某些标签阻塞了函数执行。