简单实现图片懒加载

197 阅读2分钟

「这是我参与2022首次更文挑战的第6天,活动详情查看:2022首次更文挑战」。

图片懒加载是很常用的页面优化方式,在一定程度上节省了服务器的开销,有非常多已经成熟的库可以使用,当知道了原理以后实现起来难度也不是很大,今天就来手动的实现一下

原理与实现思路

  • 当页面上有较多的图片需要展示,不用一次性加载所用图片,而是当图片出现在可视范围内再加载
  • 加载图片的过程:一般将图片url放在img标签的src属性上,页面加载到img就会立即请求图片的url,当请求成功后,就会把真实的图片渲染到页面上,请求是一个异步的过程,这和网络、浏览器的性能都有关,难免会造成白屏的情况
  • 因此一开始可以不设置img标签的src属性,先用其他的标签存放url,在需要加载的时候把url放到src属性上
// 初始化不会加载图片
<img data-src="xxx.jpg">
// 替换后加载图片
<img src="xxx.jpg">
  • 如何判定图片是否出现在可视范围内:假设浏览器可视窗口区域的高度clientHeight,图片距离窗口顶部的高度rect.top,当rect.top < clientHeight时则说明图片出现在了可视范围内,此时就可以去请求图片的url地址
// 窗口高度
var clientHeight = window.innerHeight
// 图片距离窗口顶部的高度
var rect = img.getBoundingClientRect()
if(rect.top < clientHeight) {
    // 这里去加载图片
}

代码实现

  • 初始化造一些数据
<style>
    .content {
      width: 640px;
      margin: 0 auto;
    }
    .content img {
      width: 640px;
      height: 360px;
      margin-bottom: 10px;
      background: gray; // 默认给一个背景色
    }
</style>
<div class="content">
    <img data-src="https://img9.51tietu.net/pic/2019-090921/lo24s1wwniplo24s1wwnip.jpg?v=1">
    <img data-src="https://img9.51tietu.net/pic/2019-090921/lo24s1wwniplo24s1wwnip.jpg?v=2">
    <img data-src="https://img9.51tietu.net/pic/2019-090921/lo24s1wwniplo24s1wwnip.jpg?v=3">
    <img data-src="https://img9.51tietu.net/pic/2019-090921/lo24s1wwniplo24s1wwnip.jpg?v=4">
    <img data-src="https://img9.51tietu.net/pic/2019-090921/lo24s1wwniplo24s1wwnip.jpg?v=5">
    <img data-src="https://img9.51tietu.net/pic/2019-090921/lo24s1wwniplo24s1wwnip.jpg?v=6">
    <img data-src="https://img9.51tietu.net/pic/2019-090921/lo24s1wwniplo24s1wwnip.jpg?v=7">
    <img data-src="https://img9.51tietu.net/pic/2019-090921/lo24s1wwniplo24s1wwnip.jpg?v=8">
    <img data-src="https://img9.51tietu.net/pic/2019-090921/lo24s1wwniplo24s1wwnip.jpg?v=9">
    <img data-src="https://img9.51tietu.net/pic/2019-090921/lo24s1wwniplo24s1wwnip.jpg?v=10">
</div>

监听scroll事件实现

function lazyLoad() {
  // 窗口高度
  var clientHeight = window.innerHeight
  var imgList = document.querySelectorAll('img[data-src]')
  for (var i = 0; i < imgList.length; i++) {
    var img = imgList[i]
    var rect = img.getBoundingClientRect()
    // 当前图片距离顶部小于窗口高度则加载
    if(rect.top < clientHeight - 100) {
      loadImg(img)
    }
  }
}
// 加载图片
function loadImg(el) {
  var src = el.getAttribute('data-src')
  var img = new Image()
  img.src = src
  img.onload = function () {
    el.src = src
    // 删除临时属性,下次就不会再遍历到
    el.removeAttribute('data-src')
  }
}
// 初始化没有滚动先执行一次
lazyLoad()
// 监听scroll,每滚动一次触发lazyload
document.addEventListener('scroll', lazyLoad)
  • 效果图 scroll-lazyload.gif

  • 缺点:scroll触发非常频繁,每触发一次都会去执行一次lazyload函数,在性能上存在一定的浪费

IntersectionObserver API 实现

还不知道什么是IntersectionObserver?阮一峰 IntersectionObserver API 使用教程

const observer = new IntersectionObserver(changes => {
  const clientHeight = window.innerHeight
  changes.forEach(item => {
    const { target, isIntersecting } = item
    if (isIntersecting) { //到达窗口可视区域
      loadImg(target)
      observer.unobserve(target) // 加载图片后解除观察
    }
  })
})

function loadImg(el) {
  var src = el.getAttribute('data-src')
  var img = new Image()
  img.src = src
  img.onload = function () {
    el.src = src
    el.removeAttribute('data-src')
  }
}

document.querySelectorAll('img[data-src]').forEach(item => {
  observer.observe(item)
})
  • 优点:浏览器自动监听元素是否出现在可视范围内
  • 缺点:未兼容所有浏览器