![b09736151fd622e609074c6170c927ce.gif](https://i-blog.csdnimg.cn/blog_migrate/545f9d51a19015e1a5b2b5ef0abe3c07.gif)
![0577cc67eba97b5ba65d5ca130b1ef0d.png](https://i-blog.csdnimg.cn/blog_migrate/face194d9dd0cd4a63bef3595f7fece4.jpeg)
作者 | 前端劝退师
责编 | 胡巍巍
在本篇文章你将会学到:
IntersectionObserver API的用法,以及如何兼容。
如何在React Hook中实现无限滚动。
如何正确渲染多达10000个元素的列表。
无限下拉加载技术使用户在大量成块的内容面前一直滚动查看。这种方法是在你向下滚动的时候不断加载新内容。
当你使用滚动作为发现数据的主要方法时,它可能使你的用户在网页上停留更长时间并提升用户参与度。
随着社交媒体的流行,大量的数据被用户消费。无线滚动提供了一个高效的方法让用户浏览海量信息,而不必等待页面的预加载。
如何构建一个体验良好的无限滚动,是每个前端无论是项目或面试都会碰到的一个课题。
![18fcb10ccd11897c02884fc49e8a409e.png](https://i-blog.csdnimg.cn/blog_migrate/718675435b792fea24688214c80a65db.jpeg)
早期的解决方案
关于无限滚动,早期的解决方案基本都是依赖监听scroll事件:
function fetchData {
fetch(path).then(res => doSomeThing(res.data));
}
window.addEventListener('scroll', fetchData);
然后计算各种.scrollTop、.offset.top等等。
手写一个也是非常枯燥。而且:
scroll事件会频繁触发,因此我们还需要手动节流。
滚动元素内有大量DOM,容易造成卡顿。
![c79fce4c1da5e6f871c71a029e2bbbcb.png](https://i-blog.csdnimg.cn/blog_migrate/040f66924410c35b13d32b01bf0bbce3.jpeg)
交叉观察者:IntersectionObserver
const box = document.querySelector('.box');
const intersectionObserver = new IntersectionObserver((entries) => {
entries.forEach((item) => {
if (item.isIntersecting) {
console.log('进入可视区域');
}
})
});
intersectionObserver.observe(box);
敲重点:IntersectionObserver API是异步的,不随着目标元素的滚动同步触发,性能消耗极低。
2.1 IntersectionObserverEntry对象
这里我就粗略的介绍下需要用到的:IntersectionObserverEntry对象。
callback函数被调用时,会传给它一个数组,这个数组里的每个对象就是当前进入可视区域或者离开可视区域的对象(IntersectionObserverEntry对象)
这个对象有很多属性,其中最常用的属性是:
target: 被观察的目标元素,是一个 DOM 节点对象
isIntersecting: 是否进入可视区域
intersectionRatio: 相交区域和目标元素的比例值,进入可视区域,值大于0,否则等于0
2.3 options
调用IntersectionObserver时,除了传一个回调函数,还可以传入一个option对象,配置如下属性:
threshold: 决定了什么时候触发回调函数。它是一个数组,每个成员都是一个门槛值,默认为[0],即交叉比例(intersectionRatio)达到0时触发回调函数。用户可以自定义这个数组。比如,[0, 0.25, 0.5, 0.75, 1]就表示当目标元素 0%、25%、50%、75%、100% 可见时,会触发回调函数。
root: 用于观察的根元素,默认是浏览器的视口,也可以指定具体元素,指定元素的时候用于观察的元素必须是指定元素的子元素
rootMargin: 用来扩大或者缩小视窗的的大小,使用css的定义方法,10px 10px 30px 20px表示top、right、bottom 和 left的值
const io = new IntersectionObserver((entries) => {
console.log(entries);
}, {
threshold: [0, 0.5],
root: document.querySelector('.container'),
rootMargin: "10px 10px 30px 20px