导读
相信很多小伙伴在工作中都会遇到图片请求数量过多,导致出现页面会出现卡顿的情况,在我们前端开发工作中,页面的渲染速度是十分重要的。
谈到这,我们都知道其中最大的一个问题就是图片加载速度
会拖累整个页面体验。很多页面,内容很丰富,页面很长,图片较多,比如说各种商城页面。这些页面图片数量多,而且比较大,少说百来K,多则上兆。有的时候没有必要一次性加载完。否则常会出现加载半张图片或是残缺,网速不好的有时候还会出现图片加载不出来等问题。
那么我们可以使用现有的技术,先显示可视区域
中的图片,再使剩余图片未显示的图片和滚动条事件
做交互,“滑到你,你再显示出来”,这也就是我们常说的懒加载
,来我们看一下关于懒加载的定义。
懒加载
(Load On Demand)是一种独特而又强大的数据获取
方法,它能够在用户滚动页面
的时候自动获取更多的数据,而新得到的数据不会影响原有数据的显示,同时最大程度上减少服务器端的资源耗用。
关于懒加载的方式有很多种,在这里只会着重介绍其中两种,传统的懒加载方式
以及交叉观察者
这两种方式。
传统的懒加载方式
传统的
懒加载
方式其实就是通过监听滚动轴的变化,去请求出现在可视区域的图片。
原理
将页面中的
img
标签src
指向一张小图片或者src
为空,然后定义data-src
(这个属性可以自定义命名,我采用data-src
)属性指向真实的图片。
新建一个HTML文件
<div class="box">
// 在这里我们使用自定义属性data-src存储真实的图片地址
<img style="margin-top: 600px;" class="img" src=""
data-src="https://p1-jj.byteimg.com/tos-cn-i-t2oaga2asx/gold-user-assets/2019/9/7/16d0a7d1e26e2947~tplv-t2oaga2asx-image.image"
alt="">
<img class="img" src=""
data-src="https://p1-jj.byteimg.com/tos-cn-i-t2oaga2asx/gold-user-assets/2019/9/7/16d0a7d1e28ba479~tplv-t2oaga2asx-image.image"
alt="">
<img class="img" src=""
data-src="https://p1-jj.byteimg.com/tos-cn-i-t2oaga2asx/gold-user-assets/2019/9/7/16d0a7d1e5b3c69c~tplv-t2oaga2asx-image.image"
alt="">
<img class="img" src=""
data-src="https://p1-jj.byteimg.com/tos-cn-i-t2oaga2asx/gold-user-assets/2019/9/7/16d0a7d1e60d6bc9~tplv-t2oaga2asx-image.image"
alt="">
<img class="img" src=""
data-src="https://p1-jj.byteimg.com/tos-cn-i-t2oaga2asx/gold-user-assets/2019/9/7/16d0a7d1e5ac147c~tplv-t2oaga2asx-image.image"
alt="">
<img class="img" src=""
data-src="https://p1-jj.byteimg.com/tos-cn-i-t2oaga2asx/gold-user-assets/2019/9/7/16d0a7d1e5d0a8ef~tplv-t2oaga2asx-image.image"
alt="">
<img class="img" src=""
data-src="https://p1-jj.byteimg.com/tos-cn-i-t2oaga2asx/gold-user-assets/2019/9/7/16d0a7d2033996bc~tplv-t2oaga2asx-image.image"
alt="">
</div>
新建一个CSS文件
.box {
width: 800px;
height: 400px;
border: 1px solid;
overflow: auto;
}
.img {
width: 100px;
height: 100px;
border: 1px solid;
display: block;
margin: 40px auto;
}
在这里我们基本可以实现一个这样的界面
实现懒加载
1.首先我们先去获取父级元素以及子元素
const box = document.querySelector('.box');
const imgs = document.querySelectorAll('.img');
2.监听父级滚动轴变化
box.addEventListener('scroll', handleListenScroll);
3.实现监听函数
function handleListenScroll() {
//获取父级滚动轴距离顶部距离
var scrollTop = box.scrollTop;
//获取父级高度
var divHeight = box.clientHeight;
for (var i = 0; i < imgs.length; i++){
if (imgs[i].offsetTop < divHeight + scrollTop) {
imgs[i].src = imgs[i].getAttribute('data-src');
}
}
}
这样一个简单的懒加载就实现了,我们看下效果。
优化
如果直接将函数绑定在scroll事件上,当页面滚动时,函数会被高频触发,这非常影响浏览器的性能。
所谓
节流
,就是当持续触发事件时,保证一定时间段内只调用一次事件处理函数。这里也许会有小伙伴说了,防抖
呢?先说一下防抖
的概念吧,当持续触发事件时,一定时间段内没有再触发事件,事件处理函数才会执行一次,如果设定的时间到来之前,又一次触发了事件,就重新开始延时。根据我们的场景,显然在这里使用防抖
是不太合适的。
box.addEventListener('scroll', throttle(handleListenScroll, 500));
//节流实现
function throttle(func, delay) {
var previous = 0;
return function () {
let now = Date.now();
let context = this;
let args = arguments;
if (now - previous > delay) {
func.apply(context, args);
previous = now;
}
};
}
小tip 在使用的时候我们也可以给每个图片一个默认图片,等图片出现在可视区域的时候进行一个替换。
交叉观察者
交叉观察者
其实就是利用IntersectionObserver
接口 (从属于Intersection Observer API
) 提供了一种异步观察目标元素与其祖先元素或顶级文档视窗(viewport
)交叉状态的方法。祖先元素与视窗(viewport
)被称为根(root
)。
用通俗的话来讲就是判断目标元素有没有和父级元素产生交叉行为,一张图说明一切,画的丑,请见谅~~
兼容性
呃呃呃, IE几乎全军覆没,不过,有官方提供过的polyfill
,啥都不用怕,点这里。
属性和方法
属性 | 介绍 |
---|---|
root | 所监听对象的具体祖先元素(element)。如果未传入值或值为null,则默认使用顶级文档的视窗。 |
rootMargin | 计算交叉时添加到根(root)边界盒bounding box的矩形偏移量, 可以有效的缩小或扩大根的判定范围从而满足计算需要 |
thresholds | 一个包含阈值的列表, 按升序排列, 列表中的每个阈值都是监听对象的交叉区域与边界区域的比率。 |
方法 | 介绍 |
---|---|
disconnect() | 使IntersectionObserver对象停止监听工作 |
observe() | 使IntersectionObserver开始监听一个目标元素。 |
takeRecords() | 返回所有观察目标的IntersectionObserverEntry对象数组。 |
unobserve() | 使IntersectionObserver停止监听特定目标元素。 |
使用
1.新建一个IntersectionObserver
对象。
const observer = new IntersectionObserver(entries => {
// 发生交叉目标元素集合
console.log(entries);
}, option);
在这里callback会返回给我们当前已监听并且发生了交叉的目标集合,我们看一下具体信息。
属性 | 介绍 |
---|---|
boundingClientRect | 空间信息 |
target | 目标元素 |
isIntersecting | 是否发生交叉 |
2.监听目标元素
const box = document.querySelector('.box');
const imgs = document.querySelectorAll('.img');
const observer = new IntersectionObserver(entries => {
// 发生交叉目标元素集合
entries.forEach(item => {
// 判断是否发生交叉
if (item.isIntersecting) {
// 替换目标元素Url
item.target.src = item.target.dataset.src;
// 取消监听此目标元素
observer.unobserve(item.target);
}
});
}, {
root: box, // 父级元素
rootMargin: '20px 0px 100px 0px' // 设置偏移 我们可以设置在目标元素距离底部100px的时候发送请求
});
imgs.forEach(item => {
// 监听目标元素
observer.observe(item);
});
实现
以上就是使用
IntersectionObserver
实现懒加载的过程,是不是很简单呢,在这里我们不再需要去监听滚动条的变化,只需要根据返回给我们的元素集合就可以实现。还要记住必须是子元素跟父元素发生交叉,是不可以去检查两个非父子关系的交叉的情况哦~~
综上 就是我对于懒加载一点小见解,也希望未来我们的页面更加流畅,so,先从懒加载开始吧~~。其实懒加载只是通过IntersectionObserver
实现的一点小功能,IntersectionObserver
还可以帮我们实现吸顶
、触底
这些功能,感兴趣的小伙伴可以去试试哦。最后,文中如有错误,欢迎在评论区指正,如果这篇文章帮助到了你,欢迎点赞👍和关注,😀。