图片懒加载的4种方式

201 阅读1分钟

前言 🎤

经典问题,就不用多说

图片

<img data-src ='https://static01.imgkr.com/temp/ea80321b3be5465286bd612cb9045ac4.jpg'>
<img data-src ='https://static01.imgkr.com/temp/cb9957ec31db4fb5b3f724db8fa998dc.jpg'>
<img data-src ='https://static01.imgkr.com/temp/37247ba017db4ed6aa15b8281ad97aad.jpg'>
<img data-src ='https://static01.imgkr.com/temp/763136c2aa7e49d09d53aa15d691eb0c.jpg'>
<img data-src ='https://static01.imgkr.com/temp/5b5707a9234345d7b38d029bcd86796f.jpg'>

方法一 offsetTop < clientHeight + scrollTop

{% iframe codepen.io/nishuzumi/e… %}

//HTML
<div style='height:500px'></div>
<div>
  <img data-src ='https://static01.imgkr.com/temp/ea80321b3be5465286bd612cb9045ac4.jpg'>
  <img data-src ='https://static01.imgkr.com/temp/cb9957ec31db4fb5b3f724db8fa998dc.jpg'>
  <img data-src ='https://static01.imgkr.com/temp/37247ba017db4ed6aa15b8281ad97aad.jpg'>
  <img data-src ='https://static01.imgkr.com/temp/763136c2aa7e49d09d53aa15d691eb0c.jpg'>
  <img data-src ='https://static01.imgkr.com/temp/5b5707a9234345d7b38d029bcd86796f.jpg'>
</div>

// css
img {
  width:600px;
  height:400px;
  display:block
}

// js
function check(){
  let imgs = document.querySelectorAll('img[data-src]')
  if (imgs.length === 0) return
  let viewHeight = document.documentElement.clientHeight
  let offset = document.documentElement.scrollTop
  imgs.forEach(img=>{
    if(img.offsetTop < viewHeight + offset){
      img.src = img.dataset.src
      delete img.dataset.src
    }
  })
}

window.onscroll = check
check()

window.onscroll = check
check()

这个方法有个缺点,不能适应图片经过定位(position不为static)的父级元素。

方法二 getBoundingClientRect().top < clientHeight

{% iframe codepen.io/nishuzumi/e… %} html和css内容不变

function check(){
  let imgs = document.querySelectorAll('img[data-src]')
  if (imgs.length === 0) return
  imgs.forEach(img=>{
    if (img.getBoundingClientRect().top < document.documentElement.clientHeight){
      img.src = img.dataset.src
      delete img.dataset.src
    }
  })
}

window.onscroll = check
check()

性能稍微有点落后

方法三 InterscectionObserver

let observe = new IntersectionObserver(
    function (entities) {
        entities.forEach(entity => {
            if(entity.intersectionRatio <= 0) return
            let img = entity.target
            img.src = img.dataset.src
            delete img.dataset.src
            observe.unobserve(img)
        })
    }, {
        threshold: 0.01
})

document.querySelectorAll('img').forEach(img => { observe.observe(img) })

⚠️注意,第一次监听元素的时候会直接触发一次callback,如果有多个元素,则entities是一个数组。

方法四 loading='lazy'

<img loading='lazy' src='https://static01.imgkr.com/temp/ea80321b3be5465286bd612cb9045ac4.jpg'>
<img loading='lazy' src='https://static01.imgkr.com/temp/cb9957ec31db4fb5b3f724db8fa998dc.jpg'>
<img loading='lazy' src='https://static01.imgkr.com/temp/37247ba017db4ed6aa15b8281ad97aad.jpg'>
<img loading='lazy' src='https://static01.imgkr.com/temp/763136c2aa7e49d09d53aa15d691eb0c.jpg'>
<img loading='lazy' src='https://static01.imgkr.com/temp/5b5707a9234345d7b38d029bcd86796f.jpg'>

使用loading='lazy'的时候,他的行为会稍微有些怪异。他不会等到距离视窗非常近的时候才加载,而是在有一段距离时就开始提前加载。