图片懒加载
1.什么是懒加载
懒加载是一种网页优化技术,在页面滚动时,只加载可视区域内的图片。
2.为什么要用懒加载
试想页面有一个长列表图片需要展示,用户点击链接进入页面后加载图片列表,此时不光页面加载时间长,同时在同一时间内会向服务器发送大量请求,造成服务器压力过大。所以我们可以只加载用户可视范围内的图片,其余的图片在用户滚动到可视范围内时再加载。
懒加载优点:
- 减少页面加载时间,优化用户体验
- 减少请求,减轻服务器压力
3.实现原理
监听页面滚动,判断元素是否进入可视范围,进入则加载图片,否则显示占位图片:
- 判断元素是否在可视区域:
- 我们使用
innerHeight
获取视口的高度, 使用getBoundingClientRect()
获取元素距离页面顶部的高度与元素底部距离页面顶部的高度: - getBoundingClientRect 图解:
- 当元素距离顶部的距离
eleTop
小于视口高度viewHeight
时,元素在可视区域内,加载图片:
1 |
|
- 监听滚动
我们已经知道元素是否在可视范围内,只需在滚动时判断元素是否在可视范围内即可,我们可以使用 window.onscroll
监听滚动事件,并添加防抖优化性能:
1 |
|
在页面初始化时我们还需要判断一次元素是否在可视范围,以下是实现效果:
JS 完整代码:
1 |
|
4.使用 IntersectionObserver 实现懒加载
IntersectionObserver
构造函数实现懒加载更加简单,元素在可视范围内时IntersectionObserver
对象属性isIntersecting
为 true,我们可以直接了当的判断元素是不是在可视范围内,而不用再监听页面滚动来判断元素是否再可视范围内,但是低版本的浏览器不支持该构造函数
创建 IntersectionObserver 实例
IntersectionObserver 接收两个参数:
- callback: 当元素可见比例超过指定阈值后,会调用一个回调函数,此回调函数接受两个参数:
entries:
一个 IntersectionObserverEntry 对象的数组,每个被触发的阈值,都或多或少与指定阈值有偏差。observer:
被调用的 IntersectionObserver 实例。 - options: 配置对象,可以设置以下属性:
root: 监听元素的祖先元素 Element 对象,其边界盒将被视作视口。目标在根的可见区域的任何不可见部分都会被视为不可见。
rootMargin:
一个在计算交叉值时添加至根的边界盒 (bounding_box) 中的一组偏移量,类型为字符串 (string) ,可以有效的缩小或扩大根的判定范围从而满足计算需要。语法大致和 CSS 中的 margin 属性等同; 可以参考 intersection root 和 root margin 来深入了解
margin 的工作原理及其语法。默认值是”0px 0px 0px 0px”。threshold:
规定了一个监听目标与边界盒交叉区域的比例值,可以是一个具体的数值或是一组 0.0 到 1.0 之间的数组。若指定值为 0.0,则意味着监听元素即使与根有 1 像素交叉,此元素也会被视为可见。若指定值为 1.0,则意味着整个元素都在可见范围内时才算可见。可以参考阈值来深入了解阈值是如何使用的。阈值的默认值为 0.0。
代码如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19const observer = (ele) => {
const eles = document.querySelectorAll(ele);
const observers = new IntersectionObserver(
(entries, observer) => {
entries.forEach((entry) => {
if (entry.isIntersecting) {
// 加载图片
entry.target.src = entry.target.dataset.src;
observer.unobserve(entry.target);
}
});
},
{ threshold: 1 }
);
eles.forEach((item) => {
observers.observe(item);
});
};- callback: 当元素可见比例超过指定阈值后,会调用一个回调函数,此回调函数接受两个参数:
使用 IntersectionObserver 实例监听元素
实现效果:
JS 完整代码:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22// 创建实例
// 使用IntersectionObserver API 实现懒加载
const observer = (ele) => {
const eles = document.querySelectorAll(ele);
const observers = new IntersectionObserver(
(entries, observer) => {
entries.forEach((entry) => {
if (entry.isIntersecting) {
// 加载图片
entry.target.src = entry.target.dataset.src;
observer.unobserve(entry.target);
}
});
},
{ threshold: 1 }
);
eles.forEach((item) => {
observers.observe(item);
});
};
observer("img");