实现图片懒加载的新方式-IntersectionObserver

728 阅读4分钟

这是我参与更文挑战的第12天,活动详情查看: 更文挑战

图片懒加载

定义

什么是图片懒加载? 是一种提升页面加载速度的优化方式

假设我们页面有很多图片,如果我们一进入页面就加载所有图片,现在的图片都有几百k,那加载完耗时就很久,用户都跑光了图片还没加载好,所以就有图片懒加载这种方式

图片懒加载,就是页面向下滚动,当图片快到我们的可视区域之前,才去加载图片,不在我们的可视区域我们就不加载,这就是图片懒加载,可以更好的节省带宽,提高页面加载速度。

常规实现方法

一般常规的实现方法,是监听页面的滚动事件,在快要滚动到图片的位置之前,把img的src替换成真实的src,后面的图片依次类推。简易版代码如下

    // 检查是否在可视区域
    function checkDomIsInVisibleArea(dom) {
      // 间距,就是提前多少间距就认为是在可视区域里面
      const distance = 100
      const rect = dom.getBoundingClientRect()
      return rect.top < window.innerHeight + distance && rect.top > -distance && rect.left < window.innerWidth + distance && rect.left > -distance
    }
    // 防抖函数
    function debounce(fn, delay = 100) {
      let timer = null
      return function (...args) {
        if (timer) clearTimeout(timer)
        timer = setTimeout(() => {
          fn(...args)
        }, delay)
      }
    }
    // 懒加载
    function lazyLoad() {
      const imgs = document.querySelectorAll('img')
      imgs.forEach(dom => {
        // 在可视区域并且有data-src
        if (checkDomIsInVisibleArea(dom) && dom.dataset['src']) {
          dom.setAttribute('src', dom.dataset['src'])
          dom.removeAttribute('data-src')
        }
      })
    }
    // 监听滚动事件
    window.addEventListener('scroll', debounce(lazyLoad))

今天看到还有一个原生api有个神器也可以实现,那就是IntersectionObserver,下面咱们好好来讲讲。

IntersectionObserver

翻译为交叉观察者,可以异步观察目标元素与其祖先元素或顶级文档视窗(viewport)的交叉状态,简而言之就是监听元素,如果该元素处于可视区域或者滚动经过可视区域,会触发回调函数。

IntersectionObserver API是异步的,不随着目标元素的滚动同步触发,不用担心会影响网页性能

语法

const observer = new IntersectionObserver(callback[, options]) // 返回 IntersectionObserver实例

callback 如果元素和祖先元素发生交叉,就执行此回调函数,返回2个参数

  1. entries,包含IntersectionObserverEntry对象的数组

    打印的IntersectionObserverEntry对象 image.png

    IntersectionObserverEntry对象说明如下

    属性说明
    boundingClientRect目标元素的位置信息
    intersectionRatio目标元素在可视区域的占比
    intersectionRect目标元素与可视区域的交叉位置信息
    isIntersecting目标元素是否正在可视区域
    rootBounds可视区域的位置信息
    target目标元素
    time记录时间原点到发生交叉的时间戳,毫秒

options

可选,对象类型,配置参数如下

属性说明
root当作可视区域的Element对象,默认是浏览器的视口
rootMargin扩大或者缩小可视区域的的大小,语法大致和CSS中的margin属性等同,默认是0px 0px 0px 0px
threshold规定了目标元素占可视区域多大比例,才触发callback,可以是数值或者数组。默认是0, 就是监听元素一出现在可视区域就触发,最大是1, 整个元素都出现了才触发。也可以设置数组,[0, 0.25, 1], 一进入触发(0%)回调,当进入到25%时候触发回调,整个元素都出现(100%)也触发回调

IntersectionObserver实例方法

  1. observe(Element)

    开始监听一个目标元素, 参数是目标元素的节点

  2. unobserve(Element)

    取消监听一个目标元素, 参数是目标元素的节点

  3. takeRecords()

    无参数,返回所有目标元素的IntersectionObserverEntry对象的数组

  4. disconnect()
    无参数,断开连接,停止IntersectionObserver的所有监听

图片懒加载-IntersectionObserver实现

    // 创建交叉观察实例
    const observer = new IntersectionObserver(entries => {
      entries.forEach(item => {
        // 如果正在交叉
        if (item.isIntersecting) {
          item.target.setAttribute('src', item.target.dataset['src'])
          // 取消监听
          observer.unobserve(item.target)
        }
      })
    }, {
      threshold: .1
    })
    const imgs = document.querySelectorAll('img')
    // 遍历imgs开始监听
    imgs.forEach(item => {
      // 开始监听
      observer.observe(item)
    })

兼容性

目前兼容性不是很好,查看下图,数据来自caniuse

image.png

总结

IntersectionObserver这个api很好用,图片懒加载只是其中的一个用法,还可以用来无限滚动,曝光埋点滚动动画等等,大家先学会,后面有需要就可以用上啦,希望对你们有帮助。感谢~