懒加载 是一种资源延时加载技术。它的具体实现表现为: 在满足某种条件时才加载资源

lazy loading attr

Can I use loading lazy attr?

在现代浏览器中,img 标签支持 loading 属性,它具有两个可选枚举值:

描述
lazy 当某种条件满足时加载资源
eager 立即加载资源,默认状态

用法如下:

1
<img src="https://source.unsplash.com/random/1126x260" loading="lazy" alt="Unsplash random image" />

注意:不同浏览器的具体实现方式不一样(加载资源需要满足的条件不一样),基于 chromium 实现的浏览器会比 Firfox 更早的加载资源。可以前往 stack overflow 查看相关问题讨论。

其它可参考文献

https://issues.chromium.org/issues/40577771

https://developer.mozilla.org/en-US/docs/Web/Performance/Lazy_loading

IntersectionObserver

Can I use IntersectionObserver?

相比直接使用 lazy loading attr ,使用 IntersectionObserver 可以让我们更加随心所欲的控制满足资源加载的条件。

1
2
3
4
<div class="scrollContainer">
<!-- 使用 data-src 代替 src,避免资源立即加载 -->
<img data-src="https://source.unsplash.com/random/1126x260" alt="Unsplash random image" />
</div>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
const observer = new IntersectionObserver((entries) => {
entries.forEach((entry) => {
if (entry.isIntersecting) {
entry.target.src = entry.target.dataset.src;
observer.unobserve(entry.target);
}
});
}, {
root: document.querySelect('.scrollContainer'),
rootMargin: '0px 0px 200px 0', // 控制视图范围
threshold: [0, 0, 0, 0]
});

const imgElements = document.querySelectAll('img');
Array.from(imgElements).forEach((el) => {
observer.observe(el);
});

Element.getBoundingClientRect()

Can I use getBoundingClientRect()?

Element.getBoundingClientRect() 相比 IntersectionObserver 没有那么方便,但它具有更良好的浏览器兼容性,当需要考虑浏览器兼容性的时候,使用 Element.getBoundingClientRect() 是一个不错的选择。

1
2
3
4
<div class="scrollContainer">
<!-- 使用 data-src 代替 src,避免资源立即加载 -->
<img data-src="https://source.unsplash.com/random/1126x260" alt="Unsplash random image" />
</div>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
const scrollContainer = document.querySelector('.scrollContainer');
const imgElements = scrollContainer.querySelectorAll('img');

scrollContainer.addEventListener('scroll', (event) => {
Array.from(imgElements).forEach((el) => {
if (el.dataset.src && isScrollIntoView(el)) {
el.src = el.dataset.src;
delete el.dataset.src;
}
});
});

/**
* 判断元素是否滚动到视图内
* @param {HTMLElement} el
*/
function isScrollIntoView(el) {
const rect = el.getBoundingClientRect();
if (scrollContainer.offsetTop + scrollContainer.clientHeight > rect.top) {
return true;
}
return false;
}

使用 Element.getBoundingClientRect() 需要考虑的因素很多,如:设置滚动容器、设置满足资源加载的条件等。