为什么要图片懒加载
当我们网站首页有大量图片需要加载,或者说实现一个照片墙无限向下滚动的业务。如果接口一次性给我们100条数据,我们每次全都一股脑加载100张图片,超出屏幕可是范围的图片也要占用加载时间,那用户体验一定是不友好的。 针对以上情况,我们就需要图片懒加载来解决类似问题,懒加载优点: 1,减少首屏加在资源负担,这样明显减少了不需要立即加载的资源请求 2,资源按需加载,减少用户等待不需要的资源加载的时间。
实现方法
方法一(getBoundingClientRect):
通过自己监听页面的scroll滚动事件,遍历计算每张图片是否到了可视区域内,大致步骤如下
- window.addEventListener('scroll',callback) 监听页面滚动事件
- el.getBoundingClientRect 遍历获取每个还没加载图片距离显示器原点的距离,判断图片是否在可视区域内
- 如果在则设置图片src属性,完成加载
- 监听scroll时注意添加防抖函数
然而这种检测通常要用到事件监听,并且需要频繁调用 el.getBoundingClientRect。方法以获取相关元素的边界信息。事件监听和调用el.getBoundingClientRect 都是在主线程上运行,因此频繁触发、调用可能会造成性能问题。这种检测方法极其怪异且不优雅。
假如有一个无限滚动的网页,开发者使用了一个第三方库来管理整个页面的广告,又用了另外一个库来实现消息盒子和点赞,并且页面有很多动画(译注:动画往往意味着较高的性能消耗)。两个库都有自己的相交检测程序,都运行在主线程里,而网站的开发者对这些库的内部实现知之甚少,所以并未意识到有什么问题。但当用户滚动页面时,这些相交检测程序就会在页面滚动回调函数里不停触发调用,造成性能问题,体验效果让人失望。
方法二(IntersectionObserver)
Intersection Observer API 会注册一个回调函数,每当被监视的元素进入或者退出另外一个元素时,或者两个元素的相交部分大小发生变化时,该回调方法会被触发执行。这样,我们网站的主线程不需要再为了监听元素相交而辛苦劳作,浏览器会自行优化元素相交管理。
我们接下来主要以这种方式实现懒加载
IntersectionObserver
直接上代码IntersectionObserver
<!DOCTYPE html>
<html>
<style>
@charset "UTF-8";
.imglist{
width: 500px;
height: 500px;
display: block;
}
</style>
<head></head>
<body>
<img class="imglist"
data-src="https://lf3-cdn-tos.bytescm.com/obj/static/xitu_juejin_web/img/default.640d9a7.png" />
<img class="imglist"
data-src="https://lf3-cdn-tos.bytescm.com/obj/static/xitu_juejin_web/img/default.640d9a7.png" />
<img class="imglist"
data-src="https://lf3-cdn-tos.bytescm.com/obj/static/xitu_juejin_web/img/default.640d9a7.png" />
<img class="imglist"
data-src="https://lf3-cdn-tos.bytescm.com/obj/static/xitu_juejin_web/img/default.640d9a7.png" />
<img class="imglist"
data-src="https://lf3-cdn-tos.bytescm.com/obj/static/xitu_juejin_web/img/default.640d9a7.png" />
<img class="imglist"
data-src="https://lf3-cdn-tos.bytescm.com/obj/static/xitu_juejin_web/img/default.640d9a7.png" />
<img class="imglist"
data-src="https://lf3-cdn-tos.bytescm.com/obj/static/xitu_juejin_web/img/default.640d9a7.png" />
<img class="imglist"
data-src="https://lf3-cdn-tos.bytescm.com/obj/static/xitu_juejin_web/img/default.640d9a7.png" />
<img class="imglist"
data-src="https://lf3-cdn-tos.bytescm.com/obj/static/xitu_juejin_web/img/default.640d9a7.png" />
<img class="imglist"
data-src="https://lf3-cdn-tos.bytescm.com/obj/static/xitu_juejin_web/img/default.640d9a7.png" />
<img class="imglist"
data-src="https://lf3-cdn-tos.bytescm.com/obj/static/xitu_juejin_web/img/default.640d9a7.png" />
<img class="imglist"
data-src="https://lf3-cdn-tos.bytescm.com/obj/static/xitu_juejin_web/img/default.640d9a7.png" />
</body>
<script>
window.onload = function () {
(function () {
let imgList = document.querySelectorAll(".imglist"), //获取所有要懒加载的图片node
options = {
root:null, //指定根(**root**)元素,用于检查目标的可见性。必须是目标元素的父级元素。如果未指定或者为`null`,则默认为浏览器视窗。
rootMargin: '0px',//根元素外边距,用于计算交际时的区域范围
threshold: 0 //阈值,父子元素交集大小超过该值就会执行回调函数,默认为0
};
let observer = new IntersectionObserver(imgAction, options); //创建一个 IntersectionObserver 对象
//对目标图片元素进行观察
function listenImg(target) {
observer.observe(target);
}
//当元素与窗口交集发生变化时的回调
function imgAction(entries, observer) {
entries.forEach(entry => {
if (entry.isIntersecting) { //判断达到阈值,产生交集
let node = entry.target;//获取element
node.src = node.dataset.src;//赋值src属性
node.removeAttribute('data-src');//删除data-src属性
observer.unobserve(entry.target)// 已经完成加载就,解除观察
}
});
}
//初始化观察所有的图片
imgList.forEach((img) => {
listenImg(img);
})
})()
}
</script>
</html>
兼容性
目前pc端ie暂不支持,移动端safri部分支持
总结
- IntersectionObserver极为高效简单的实现了异步对两个元素交集的判断,甚至能轻松获取到具体交叉的数值
- 此api目前现代浏览器已基本兼容,但ie还不兼容
- 应用场景一般有:
- 图片懒加载
- 动画出现在可视区域启动,移出可视区域停止播放(曾经做svga动画播放性能消耗非常大需要按需播放) 3.内容无限滚动