转载自腾讯云,原文链接:https://cloud.tencent.com/developer/article/1509379
前言
在本篇文章你将会学到:
IntersectionObserver API
的用法,以及如何兼容。- 如何在
React Hook
中实现无限滚动。 - 如何正确渲染多达10000个元素的列表。
无限下拉加载技术使用户在大量成块的内容面前一直滚动查看。这种方法是在你向下滚动的时候不断加载新内容。
当你使用滚动作为发现数据的主要方法时,它可能使你的用户在网页上停留更长时间并提升用户参与度。
随着社交媒体的流行,大量的数据被用户消费。无线滚动提供了一个高效的方法让用户浏览海量信息,而不必等待页面的预加载。
如何构建一个体验良好的无限滚动,是每个前端无论是项目或面试都会碰到的一个课题。
本文的原版实现来自:Creating Infinite Scroll with 15 Elements
1. 早期的解决方案
关于无限滚动,早期的解决方案基本都是依赖监听scroll事件:
1 | function fetchData() { |
然后计算各种.scrollTop()
、.offset().top
等等。
手写一个也是非常枯燥。而且:
scroll
事件会频繁触发,因此我们还需要手动节流。- 滚动元素内有大量
DOM
,容易造成卡顿。
后来出现交叉观察者IntersectionObserver API
,在与Vue
、React
这类数据驱动视图的框架后,无限滚动的通用方案就出来了。
2. 交叉观察者:IntersectionObserver
1 | const box = document.querySelector('.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的值
1 | const io = new IntersectionObserver((entries) => { |
2.4 observer
1 | observer.observer(nodeone); //仅观察nodeOne |
3. 如何在React Hook中使用IntersectionObserver
在看Hooks
版之前,来看正常组件版的:
1 | class SlidingWindowScroll extends React.Component { |
众所周知,React 16.x
后推出了useRef
来替代原有的createRef
,用于追踪DOM节点。那让我们开始吧:
4. 原理
实现一个组件,可以显示具有15个元素的固定窗口大小的n个项目的列表: 即在任何时候,无限滚动n元素上也仅存在15个DOM
节点。
- 采用
relative/absolute
定位来确定滚动位置 - 追踪两个
ref
:top/bottom
来决定向上/向下滚动的渲染与否 - 切割数据列表,保留最多15个DOM元素。
5. useState 声明状态变量
我们开始编写组件SlidingWindowScrollHook
:
1 | const THRESHOLD = 15; |
1. useState 的简单理解:
1 | const [属性, 操作属性的方法] = useState(默认值); |
2. 变量解析
start
:当前渲染的列表第一个数据,默认为0end
: 当前渲染的列表最后一个数据,默认为15observer
: 当前观察的视图ref
元素
6. useRef 定义追踪的DOM 元素
1 | const $bottomElement = useRef(); |
正常的无限向下滚动只需关注一个dom元素,但由于我们是固定15个dom
元素渲染,需要判断向上或向下滚动。
7. 内部操作方法和和对应useEffect
请配合注释食用:
1 | useEffect(() => { |
8. 渲染界面
1 | const {list, height} = props; // 数据,节点高度 |
9. 如何使用
App.js
:
1 | import React from 'react'; |
定义一下数据 Constants.js
:
1 | const MY_ENDLESS_LIST = [ |
SlidingWindowScrollHook.js
:
1 | import React, { useState, useEffect, useRef } from "react"; |
以及少许样式:
1 | .li-card { |
然后你就可以慢慢耍了。。。
10. 兼容性处理
IntersectionObserver
不兼容Safari
?
莫慌,我们有polyfill
版
每周34万下载量呢,放心用吧臭弟弟们。
项目源地址:https://github.com/roger-hiro/SlidingWindowScrollHook
参考文章:
- Creating Infinite Scroll with 15 Elements
- IntersectionObserve初试