关于懒加载

本博客 hjy-xh,转载请申明出处

懒加载概念

懒加载也叫延迟加载,指的是在长网页中延迟加载图像,是一种很好优化网页性能的方式

用户滚动到它们之前不加载,也就是在可视区域之外的图像不会加载。

使用场景和优势

在一些电商类的项目中,往往存在大量的图片,比如广告图、商品图等等。图片的数量和体积会影响页面加载速度,而进行图片懒加载优化可以大幅提升用户体验

  • 减少无效资源的加载

  • 防止并发加载的资源过多会阻塞JS的加载

可以看出来,懒加载的核心目的就是提升用户的体验

实现

思路一

将页面上的图片的 src 属性设置为项目中引入一个默认的图片路径,而图片的真实路径则设置在自定义数据属性中,比如说data-original, 同时监听scroll事件,在scroll事件的回调中,判断需要进行懒加载的图片是否进入可视区域,如果图片在可视区内将图片的 src 属性设置为data-original 的值

代码示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
<html lang="en">

<head>
<meta charset="UTF-8">
<title>Lazyload</title>
<style>
.image-item {
display: block;
margin-bottom: 50px;
height: 200px;
}
</style>
</head>

<body>
<img src="./images/loading.png" class="image-item" lazyload="true" data-src="images/1.png" />
<img src="./images/loading.png" class="image-item" lazyload="true" data-src="images/2.png" />
<img src="./images/loading.png" class="image-item" lazyload="true" data-src="images/3.png" />
<img src="./images/loading.png" class="image-item" lazyload="true" data-src="images/4.png" />
<img src="./images/loading.png" class="image-item" lazyload="true" data-src="images/5.png" />
<img src="./images/loading.png" class="image-item" lazyload="true" data-src="images/6.png" />
<img src="./images/loading.png" class="image-item" lazyload="true" data-src="images/7.png" />
<img src="./images/loading.png" class="image-item" lazyload="true" data-src="images/8.png" />
<img src="./images/loading.png" class="image-item" lazyload="true" data-src="images/9.png" />
<img src="./images/loading.png" class="image-item" lazyload="true" data-src="images/10.png" />
<img src="./images/loading.png" class="image-item" lazyload="true" data-src="images/11.png" />
<img src="./images/loading.png" class="image-item" lazyload="true" data-src="images/12.png" />
<script>
// 获取可视区高度
const viewHeight = document.body.clientHeight;

// 懒加载
function lazyload() {
const imgs = document.querySelectorAll("img[data-src][lazyload]");
Array.prototype.forEach.call(imgs, function (item, index) {
let rect = null;
if (item.dataset.original === "") return;
// 用于获得页面中某个元素的左,上,右和下分别相对浏览器视窗的位置
rect = item.getBoundingClientRect();
if (rect.bottom >= 0 && rect.top < viewHeight) {
const img = new Image();
img.src = item.dataset.original;
img.onload = function () {
item.src = img.src;
};
// 移除属性,下次不再遍历
item.removeAttribute("data-src");
// 标记已懒加载
item.removeAttribute("lazyload");
}
});
}

// 节流,性能优化
function throttle(fn, delay) {
let tiemr = null;
return function (args) {
let that = this;
let _args = args;
if (!tiemr) {
timer = setTimeout(function () {
fn.call(that, _args);
timer = null;
}, delay);
}

}
}

// 刚开始还没滚动屏幕时,要先触发一次函数,初始化首页的页面图片
lazyload();
document.addEventListener("scroll", throttle(lazyload, 200));
</script>
</body>

</html>

思路二

思路一需要去监听 scroll 事件,虽然通过函数节流的方式来阻止高频率的执行函数,但是还是需要去计算高度等属性,而 IntersectionObserver 接口提供了一种异步观察目标元素与其祖先元素或顶级文档视口(viewport)交叉状态(可见)的方法。

相关API介绍

代码示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
<html lang="en">

<head>
<meta charset="UTF-8">
<title>Lazyload</title>
<style>
.image-item {
display: block;
margin-bottom: 50px;
height: 200px;
}
</style>
</head>

<body>
<img src="./images/loading.png" class="image-item" lazyload="true" data-src="images/1.png" />
<img src="./images/loading.png" class="image-item" lazyload="true" data-src="images/2.png" />
<img src="./images/loading.png" class="image-item" lazyload="true" data-src="images/3.png" />
<img src="./images/loading.png" class="image-item" lazyload="true" data-src="images/4.png" />
<img src="./images/loading.png" class="image-item" lazyload="true" data-src="images/5.png" />
<img src="./images/loading.png" class="image-item" lazyload="true" data-src="images/6.png" />
<img src="./images/loading.png" class="image-item" lazyload="true" data-src="images/7.png" />
<img src="./images/loading.png" class="image-item" lazyload="true" data-src="images/8.png" />
<img src="./images/loading.png" class="image-item" lazyload="true" data-src="images/9.png" />
<img src="./images/loading.png" class="image-item" lazyload="true" data-src="images/10.png" />
<img src="./images/loading.png" class="image-item" lazyload="true" data-src="images/11.png" />
<img src="./images/loading.png" class="image-item" lazyload="true" data-src="images/12.png" />
<script>
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')
img.removeAttribute("lazyload");
}
// 解除观察
self.unobserve(entry.target)
}
})
}, config)

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

</script>
</body>

</html>