图片懒加载:为什么它对网页性能和用户体验如此重要?

目录

引入

实现方式

html 实现

javaScript实现

IntersectionObserver


引入

图片的体积和数量对网页性能影响很大,特别是对于移动设备用户或者网络连接速度较慢的用户来说。懒加载是一种重要的性能优化方式,它仅在用户需要时才加载内容,而不是一次性加载所有资源。

当用户打开一个页面时,如果立即请求并加载所有图片资源,会导致页面加载速度变慢,延长了用户等待时间。同时,对于用户来说,他们可能不会立即滚动页面或者浏览所有的内容,这就意味着一些资源被加载但实际上并没有被用户看到,这样的加载就是浪费的。

因此,懒加载就是在用户滚动页面或者进行特定动作时,才加载当前视口内可见区域的内容。这样一来,页面加载速度更快,用户能够更快地看到所需的内容,同时也减少了不必要的网络流量消耗,提升了整体的用户体验。

实现方式

html 实现

当涉及图片懒加载,最简单的方式是在 img 标签中添加 loading="lazy" 属性,例如:

<img src="./example.jpg" loading="lazy">

这个属性告诉浏览器这张图片是懒加载的,意味着它会在用户接近或者滚动到图片位置时才开始加载。这种方式十分简单,并且在兼容性方面表现良好,适用于绝大多数生产环境。这种 HTML 属性的使用能够快速地实现图片懒加载,提升页面加载速度和用户体验。

javaScript实现

通过 JavaScript 实现图片懒加载的原理是检测图片是否进入了用户可视区域:

1️⃣获取页面中所有需要懒加载的图片元素。

2️⃣监听页面滚动事件,检测每个图片是否进入了可视区域。

3️⃣一旦图片进入可视区域,将 data-src 中的图片链接赋值给 src 属性,触发图片加载。

HTML 中,图片的 src 属性通常是空的,而真实的图片链接存放在 data-src 属性中。当图片进入可视范围时,JavaScript 将 data-src 的值赋给 src,触发图片加载。

对于背景图的实现,类似的原理,但是使用了 background-image 属性,图片链接存放在 data-src 中,进入可视范围时将 data-src 的值赋给 background-image,触发背景图片加载。

HTML 部分示例:

<!-- 图片懒加载 -->
<img data-src="http://xx.com/xx.png" src="" />

<!-- 背景图懒加载 -->
<div
  data-src="http://xx.com/xx.png"
  style="background-image: none;background-size: cover;"
></div>

JavaScript 会监听滚动事件,当图片元素进入用户的可视区域时,将 data-src 中的图片链接赋值给 src 或者 background-image 属性,从而实现图片或背景图的加载。

但是往往监听页面滚动事件会导致事件过多地被触发,因此会存在很大的性能问题,这时候我们可以使用节流函数来控制函数触发的频率。

下面我们来举一个例子:

<html lang="en">
<head>
  <meta charset="UTF-8" />
  <title>Lazyload</title>
  <style>
    img {
      display: block;
      margin-bottom: 50px;
      height: 200px;
      width: 400px;
    }
  </style>
</head>

<body>
  <img src="" data-src="./课程代码/课程设计/image/background2.jpg" />
  <img src="" data-src="./课程代码/课程设计/image/background2.jpg" />
  <img src="" data-src="./课程代码/课程设计/image/background2.jpg" />
  <img src="" data-src="./课程代码/课程设计/image/background2.jpg" />
  <img src="" data-src="./课程代码/课程设计/image/background2.jpg" />
  <img src="" data-src="./课程代码/课程设计/image/background2.jpg" />
  <img src="" data-src="./课程代码/课程设计/image/background2.jpg" />
  <img src="" data-src="./课程代码/课程设计/image/background2.jpg" />
  <img src="" data-src="./课程代码/课程设计/image/background2.jpg" />
  <img src="" data-src="./课程代码/课程设计/image/background2.jpg" />
  <img src="" data-src="./课程代码/课程设计/image/background2.jpg" />
  <img src="" data-src="./课程代码/课程设计/image/background2.jpg" />
  <img src="" data-src="./课程代码/课程设计/image/background2.jpg" />
  <img src="" data-src="./课程代码/课程设计/image/background2.jpg" />
  <img src="" data-src="./课程代码/课程设计/image/background2.jpg" />
  <img src="" data-src="./课程代码/课程设计/image/background2.jpg" />
</body>

</html>

我们现在页面上需要渲染很多的图片,图片的src先设置为空的字符串,将具体的路径设置在data-src属性中。接着我们编写页面滚动事件。

 function lazyload() {
      //获取可视区高度,兼容不同浏览器
      let viewHeight = window.innerHeight || document.documentElement.clientHeight || document.body.clientHeight 
      let imgs = document.querySelectorAll('img[data-src]')
      imgs.forEach((item, index) => {
        if (item.dataset.src === '') return

        // 用于获得页面中某个元素的左,上,右和下分别相对浏览器视窗的位置
        let rect = item.getBoundingClientRect()
        if (rect.bottom >= 0 && rect.top < viewHeight) {
          item.src = item.dataset.src
          item.removeAttribute('data-src')
        }
      })
    }

lazyload 函数是用来实现图片懒加载的。它首先获取可视区的高度,然后选取所有带有 data-src 属性的图片元素。对每张图片,它使用 getBoundingClientRect() 方法来获取图片相对于视口的位置信息。如果图片底部已进入视口并且顶部在视口内,那么将 data-src 中的图片链接赋给 src 属性,触发图片加载。加载后,移除 data-src 属性,以防止重复加载。这个逻辑基于检测图片是否进入可视区域来决定是否加载图片,从而实现图片懒加载的效果。

function throttle(fn, delay) {
      let timer
      let prevTime
      return function (...args) {
        const currTime = Date.now()
        const context = this
        if (!prevTime) prevTime = currTime
        clearTimeout(timer)

        if (currTime - prevTime > delay) {
          prevTime = currTime
          fn.apply(context, args)
          clearTimeout(timer)
          return
        }

        timer = setTimeout(function () {
          prevTime = Date.now()
          timer = null
          fn.apply(context, args)
        }, delay)
      }
    }
    window.addEventListener('scroll', throttle(lazyload, 200))

接着我们编写节流函数以及对其进行调用。

节流函数接受两个参数:fn 是需要被节流的函数,delay 是指定的时间间隔,在这个间隔内控制函数执行。函数内声明了两个变量,timer 用于存储定时器的引用,prevTime 用于存储上一次函数执行的时间戳。throttle 函数返回一个新的函数,这个函数用于包裹原始函数 fn

返回的函数里面获取当前时间戳 currTime,并存储当前函数执行时的上下文 context。判断如果 prevTime 不存在(也就是第一次执行函数),则将 prevTime 设置为当前时间戳。然后清除现有的定时器 timer。如果当前时间距离上一次函数执行的时间间隔大于指定的 delay,则说明可以执行函数。更新 prevTime 为当前时间戳,并且立即执行函数 fn,清除定时器以确保函数只执行一次。

如果两次函数执行的时间间隔小于指定的 delay,则设定一个新的定时器,在 delay 时间后执行函数 fn。这个定时器确保在 delay 时间内只有一次函数执行。在定时器内部,更新 prevTime 为当前时间戳,清空定时器引用,然后执行函数 fn

IntersectionObserver

IntersectionObserver 是一种API,用于自动观察页面中元素的可见性。它主要检测目标元素是否进入了浏览器视口,即目标元素与视口产生的交叉区域。这个 API 的核心在于观察器(IntersectionObserver),它能够监测元素的可见状态,并在状态变化时触发指定的回调函数。

这里有几个关键的方法和概念:

1️⃣ 创建观察器:

var io = new IntersectionObserver(callback, options)

使用 IntersectionObserver 构造函数来创建一个观察器对象。它接收两个参数:callback 是可见性变化时触发的回调函数,options 是一个可选的配置对象,用于定义观察器的行为。

2️⃣ 开始观察元素:

io.observe(document.getElementById('example'))

通过观察器对象调用 observe 方法开始观察特定的元素。一旦观察器开始工作,它会持续检测目标元素与视口的交叉情况。

3️⃣ 停止观察元素:

io.unobserve(element)

使用 unobserve 方法停止观察特定的元素,当目标元素不再需要被观察时可以调用该方法。

4️⃣ 关闭观察器:

io.disconnect()

disconnect 方法用于关闭观察器,停止所有的观察。

观察器的回调函数 callback 在目标元素进入视口和离开视口时各被触发一次。这意味着一旦目标元素开始可见或者完全不可见,观察器就会调用相应的回调函数,让开发者可以根据元素的可见状态执行相应的操作。我们对以上的例子使用IntersectionObserver来进行改写:

const imgs = document.querySelectorAll('img[data-src]')
const config = {
  rootMargin: '0px',
  threshold: 0,
}
let observer = new IntersectionObserver((entries, self) => {
  entries.forEach((entry) => {
    if (entry.isIntersecting) {
      let img = entry.target
      let src = img.dataset.src
      if (src) {
        img.src = src
        img.removeAttribute('data-src')
      }
      // 解除观察
      self.unobserve(entry.target)
    }
  })
}, config)

imgs.forEach((image) => {
  observer.observe(image)
})

首先使用 document.querySelectorAll 方法选择所有带有 data-src 属性的图片元素。config 对象定义了观察器的配置选项。rootMargin 设置为 '0px' 表示不考虑任何额外边距,threshold 设置为 0 表示当目标元素的任何部分进入视口时就会触发回调。

使用 IntersectionObserver 构造函数创建了一个观察器对象 observer。这个观察器对象接收一个回调函数和配置对象作为参数。每当被观察的元素进入或离开视口时,这个回调函数会被调用。对于每个进入视口的元素,检查其是否已经进入视口(entry.isIntersecting),如果是,则将 data-src 中的图片链接赋值给 src 属性,从而加载图片。加载后,移除 data-src 属性,避免重复加载,然后解除对该元素的观察。

最后遍历带有 data-src 属性的图片元素,并将它们逐个传递给观察器对象 observer 进行观察。一旦图片元素进入视口,就会触发上述定义的回调函数,实现图片的懒加载。

好啦,本文就到这里啦~~~

  • 23
    点赞
  • 24
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值