目录
引入
实现方式
html 实现
javaScript实现
IntersectionObserver
图片的体积和数量对网页性能影响很大,特别是对于移动设备用户或者网络连接速度较慢的用户来说。懒加载是一种重要的性能优化方式,它仅在用户需要时才加载内容,而不是一次性加载所有资源。
当用户打开一个页面时,如果立即请求并加载所有图片资源,会导致页面加载速度变慢,延长了用户等待时间。同时,对于用户来说,他们可能不会立即滚动页面或者浏览所有的内容,这就意味着一些资源被加载但实际上并没有被用户看到,这样的加载就是浪费的。
因此,懒加载就是在用户滚动页面或者进行特定动作时,才加载当前视口内可见区域的内容。这样一来,页面加载速度更快,用户能够更快地看到所需的内容,同时也减少了不必要的网络流量消耗,提升了整体的用户体验。
当涉及图片懒加载,最简单的方式是在 img
标签中添加 loading="lazy"
属性,例如:
这个属性告诉浏览器这张图片是懒加载的,意味着它会在用户接近或者滚动到图片位置时才开始加载。这种方式十分简单,并且在兼容性方面表现良好,适用于绝大多数生产环境。这种 HTML 属性的使用能够快速地实现图片懒加载,提升页面加载速度和用户体验。
通过 JavaScript 实现图片懒加载的原理是检测图片是否进入了用户可视区域:
1️⃣获取页面中所有需要懒加载的图片元素。
2️⃣监听页面滚动事件,检测每个图片是否进入了可视区域。
3️⃣一旦图片进入可视区域,将 data-src
中的图片链接赋值给 src
属性,触发图片加载。
HTML 中,图片的 src
属性通常是空的,而真实的图片链接存放在 data-src
属性中。当图片进入可视范围时,JavaScript 将 data-src
的值赋给 src
,触发图片加载。
对于背景图的实现,类似的原理,但是使用了 background-image
属性,图片链接存放在 data-src
中,进入可视范围时将 data-src
的值赋给 background-image
,触发背景图片加载。
HTML 部分示例:
JavaScript 会监听滚动事件,当图片元素进入用户的可视区域时,将 data-src
中的图片链接赋值给 src
或者 background-image
属性,从而实现图片或背景图的加载。
但是往往监听页面滚动事件会导致事件过多地被触发,因此会存在很大的性能问题,这时候我们可以使用节流函数来控制函数触发的频率。
下面我们来举一个例子:
Lazyload
我们现在页面上需要渲染很多的图片,图片的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 是一种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
进行观察。一旦图片元素进入视口,就会触发上述定义的回调函数,实现图片的懒加载。
好啦,本文就到这里啦~~~