在react当中利用IntersectionObserve实现下拉加载数据

目录

一、传统的下拉加载方案

二、存在问题

1.性能较差

2.不够精确

三、IntersectionObserve版本下拉加载

1、callback

2、options

四、IntersectionObserver实例

1、Intersection的优势

2、实现思路

3、代码实现


在进行前端开发的过程中,常常会碰到下拉加载列表数据的需求。本文将介绍如何利用Intersection API实现一个简单的下拉加载数据的demo。

一、传统的下拉加载方案

传统的下拉加载方案大多数都是通过监听scroll事件,然后获取目标元素坐标以及相关数据,再进行对应的实现。例如下面就是一个依赖数据列表容器的scrollHeightscrollTopheight实现的下拉加载的demo。

function App() {// 用于记录当前是否正在请求中const loadingRef = useRef<boolean>(false);// 列表容器const containerRef = useRef<HTMLDivElement>(null);const [dataList, setDataList] = useState([]);useEffect(() => {fetchData();}, []);useEffect(() => {const { height } = containerRef.current.getBoundingClientRect();const scrollHeight = containerRef.current.scrollHeight;const onScroll = () => {console.log('scrollHeight:', scrollHeight, 'scrollTop:', containerRef.current.scrollTop, 'height:', height);if (scrollHeight - containerRef.current.scrollTop - 1 <= height) {// 当容器已经拉到最底部时,发起请求fetchData();}};containerRef.current.addEventListener('scroll', onScroll);return () => {containerRef.current.removeEventListener('scroll', onScroll);};}, []);const fetchData = () => {// 模拟数据请求// 如果当前正在请求中,直接返回if (loadingRef.current) return;// 标记当前正在请求中loadingRef.current = true;setTimeout(() => {setDataList(_dataList => {const dataList = [..._dataList];for (let i = 0; i < 20; i++) {dataList.push(Math.random());}return dataList;});loadingRef.current = false;}, 500);};return (<div ref={containerRef} className="list-container">{dataList.map(item => (<p className="list-item" key={item}>{item}</p>))}<div className="loading">loading...</div></div>);
}

二、存在问题

1.性能较差

我们知道,scroll事件的发生是十分密集的,在监听scroll事件的回调函数中,我们都要重新获取列表容器的scrollTop,这会导致“重排”的发生。此时需要我们额外去做一些防抖或是节流的工具,防止造成性能问题。

// 节流
throttle(onScroll, 500);

2.不够精确

scrollTop的小数问题 眼尖的同学可能已经看到的,我们在判断容器是否已经滚动到底部是,还做了一个-1的操作。

if (scrollHeight - containerRef.current.scrollTop - 1 <= height) {// 当容器已经拉到最底部时,发起请求fetchData();
}

这是因为在使用显示比例缩放的系统上,scrollTop可能会提供一个小数。如下图所示,在容器滚动到底部时,scrollHeight(1542) - scrollTop(1141.5999755859375) 与容器的高度height(400)并不相等。

所以我们需要做出相应的兼容处理。

三、IntersectionObserve版本下拉加载

IntersectionObserver 提供了一种异步观察目标元素在其祖先元素或顶级文档视窗(viewport)中是否可视的方法。

IntersectionObserver的用法十分简单,我们只需要定义好DOM元素的可视状态发生变化后需要做些什么,以及需要观察哪些元素的可视状态就好了。

接下来我们详细的看看intersectionObserver这个API。

const intersectionObserver = new IntersectionObserver(callback, options?) ;

 IntersectionObserver构造函数会接收两个参数。

1、callback

callback为被观察元素的可视状态发生变更后的回调函数,此回调函数接受两个参数:

function callback(entries, observer?) => {//...
}

entries:一个IntersectionObserverEntry对象的数组。IntersectionObserverEntry对象用于描述被观察对象的可视状态的变化,拥有以下的属性:

  • entry.boundingClientRect:被观察元素的边界信息,相当于被观察元素调用getBoundingClientRect()的结果。
  • entry.intersectionRatio:被观察元素与容器元素相交矩形面积与被观察元素总面积的比例。
  • entry.intersectionRect:相交矩形的边界信息。
  • entry.isIntersecting:一个布尔值,表示被观察元素是否可视,如果是true,则表示元可视,反之则表示不可视。
  • entry.rootBounds:容器元素的边界信息,相当于容器元素调用getBoundingClientRect()的结果。
  • entry.target:被观察的元素的引用。
  • entry.time:当前时间戳。

observer:当前IntersectionObserver实例的引用。

2、options

options为一个可选参数,可传入以下属性:

  • root:指定容器元素,默认为浏览器窗体元素。容器元素必须是目标元素的祖先节点。
  • rootMargin:用于扩展或缩小rootBounds的大小,用法与CSS中margin一致,默认值为默认值是"0px 0px 0px 0px"。
  • threshold:number或number数组,用于指定callback回调函数执行的阈值,如传入[0, 0.2, 0.6, 0.8, 1]时,intersectionRatio每增加或减少0.2时都会触发回调函数的执行。默认值为0。需要注意的时,由于回调函数时异步触发的,在回调函数执行时intersectionRatio可能已经和指定的阈值不一致了。

四、IntersectionObserver实例

IntersectionObserver构造函数会把options中的属性挂载到IntersectionObserver实例上,并赋予IntersectionObserver实例四个方法:

  • IntersectionObserver.disconnect():停止监听工作。
  • IntersectionObserver.observe(targetElem):开始监听某个元素可视状态的变化。
  • IntersectionObserver.takeRecords():返回所有观察目标的IntersectionObserverEntry对象数组。
  • IntersectionObserver.unobserve(targetElem):停止监听某个目标元素。

1、Intersection的优势

intersectionObserver构造函数中传入的回调函数只会在观察的元素的可视状态发生变化后才会执行,很好的解决传统判断可视的方案的性能瓶颈。

2、实现思路

我们在实现下拉加载功能时,当数据列表还没有加载完时,我们往往会在数据列表的最后放置一个loading组件,表示当数据列表还有更加数据,并且正在加载中。我们可以利用这个loading组件的可视状态以及Intersection API实现Intersection版本的下拉加载。

3、代码实现

实现一个DemoList.tsx

import { useEffect, useRef, useState } from 'react';const DemoList = () => {// 用于记录当前是否正在请求中const loadingRef = useRef<boolean>(false);// loading divconst loadingDivRef = useRef<HTMLDivElement | null>(null);const observerRef = useRef<IntersectionObserver | null>(null);const [dataList, setDataList] = useState<number[]>([]);const fetchData = () => {// 模拟数据请求// 如果当前正在请求中,直接返回if (loadingRef.current) return;// 标记当前正在请求中loadingRef.current = true;setTimeout(() => {setDataList((_dataList) => {const dataList = [..._dataList];for (let i = 0; i < 100; i++) {// 这里面要注意的是把最新的请求的数据合并的时候要放在最后面,也就是说从数据的最后面添加。否则,就会出现连续请求的状况。原因在于如果把最新的请求对的数据放最前面的话,新增的元素是从上面渲染,就会导致下面的加载元素一直处于可见的状态,从而导致连续触发的状况dataList.push(Math.random());}return dataList;});loadingRef.current = false;}, 500);};useEffect(() => {fetchData();}, []);useEffect(() => {const target = loadingDivRef.current;if (!target) return;observerRef.current = new IntersectionObserver(function (entries) {if (entries[0].intersectionRatio > 0) {// intersectionRatio大于0,代表监听的元素由不可见变成可见,进行数据请求fetchData();}});// 监听Loading div的可见性if (loadingDivRef.current) observerRef.current.observe(loadingDivRef.current);return () => {if (observerRef.current) {if (target) observerRef.current.unobserve(target);observerRef.current.disconnect();observerRef.current = null;}};}, []);return (<div className="list-container">{dataList.map((item, index) => (<p className="list-item" key={item}>{index}——{item}</p>))}<div ref={loadingDivRef} className="loading">loading...</div></div>);
};export default DemoList;

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/bicheng/73629.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

深入理解C++编程:从内存管理到多态与算法实现

C 是一门功能强大的编程语言&#xff0c;广泛应用于系统编程、游戏开发和高性能计算等领域。本文将通过一系列经典问题&#xff0c;深入探讨 C 的核心知识点&#xff0c;包括内存管理、多态&#xff08;结合函数重载与覆盖&#xff09;、多线程、TCP/IP 模型、软链接与硬链接的…

相对论之光速

然而&#xff0c;基础物理学的进步很少全部由实验取得。为了解实验结果背后的机制&#xff0c;法拉第问道&#xff0c;既然磁铁没有接触导线&#xff0c;导线中怎么会产生电流?一股电流又怎么能使指南针指针发生偏转?有某种作用因素必然在磁铁、导线和指南针之间的空隙中传递…

文本检测-文本内容审核-文本过滤接口如何用PHP调用?

一、什么是文本检测接口呢&#xff1f; 文本内容审核过滤&#xff0c;提供对敏感事件、违规词语及监管要求封禁词语的识别审核能力&#xff0c;包含海量历史数据&#xff0c;有效过滤违禁违规、恶意推广、低俗辱骂、低质灌水、广告法审核&#xff0c;该接口应用场景广泛&#…

突破极限:猎板PCB在HDI盲埋孔树脂塞孔工艺中的创新与挑战

在高端电子制造领域&#xff0c;HDI&#xff08;高密度互连&#xff09;技术凭借其高精度、高可靠性的特点&#xff0c;已成为5G通信、航空航天、智能汽车等领域的核心技术支撑。作为HDI板制造的核心环节&#xff0c;盲埋孔树脂塞孔工艺直接决定了电路板的信号完整性、散热性能…

群体智能优化算法-䲟鱼优化算法 (Remora Optimization Algorithm, ROA,含Matlab源代码)

摘要 䲟鱼优化算法&#xff08;Remora Optimization Algorithm&#xff0c;ROA&#xff09;是一种基于䲟鱼在海洋中寄生与捕食者间交互关系而提出的元启发式算法。通过模拟䲟鱼在宿主附近进行寄生、吸附和随机机动等行为&#xff0c;ROA 在全局与局部搜索之间取得平衡。本文提…

【数学建模】一致矩阵的应用及其在层次分析法(AHP)中的性质

一致矩阵在层次分析法(AHP)中的应用与性质 在层次分析法(AHP)中&#xff0c;一致矩阵是判断矩阵的一种理想状态&#xff0c;它反映了决策者判断的完全合理性和一致性&#xff0c;也就是为了避免决策者认为“A比B重要&#xff0c;B比C重要&#xff0c;但是C又比A重要”的矛盾。…

DeepSeek R1 与 ktransformers:结合苹果 M4 Mac 的 LLM 推理深度分析

引言 大型语言模型&#xff08;LLM&#xff09;的快速发展为人工智能领域带来了革命性变化。DeepSeek R1 和 ktransformers 代表了软件层面的最新突破&#xff0c;而苹果在 2025 年 3 月 12 日发布的 M4 Mac 系列则提供了硬件支持。本文将深入分析这些技术的交汇点&#xff0c…

JavaScript基本知识

文章目录 一、JavaScript基础1.变量&#xff08;重点&#xff09;1-1 定义变量及赋值1-2 变量的命名规则和命名规范判断数据类型&#xff1a; 2.数据类型转换2-1 其他数据类型转成数值2-2 其他数据类型转成字符串2-3 其他数据类型转成布尔 3.函数3-1函数定义阶段3-2函数调用阶段…

[IP]UART

UART 是一个简易串口ip&#xff0c;用户及配置接口简单。 波特率从9600至2000000。 该 IP 支持以下特性&#xff1a; 异步串行通信&#xff1a;标准 UART 协议&#xff08;1 起始位&#xff0c;8 数据位&#xff0c;1 停止位&#xff0c;无奇偶校验&#xff09;。 参数化配置…

K8s集群的环境部署

1.测试环境所需要的主机名和IP和扮演的角色 harbor 172.25.254.200 harbor仓库 k8s-master 172.25.254.100 k8s集群控制节点 k8s-node1 172.25.254.10 k8s集群工作节点 k8s-node2 172.25.254.20 k8集群工作节点 注意&#xff1a;所有节点禁用selinux和防火墙 所有节点同步…

pytest自动化测试[面试篇]

pytest是python的测试框架&#xff0c;它提供了许多功能&#xff0c; 测试运行 组织pytest的测试用例代码&#xff1a;模块名称以test_开头&#xff0c;类名以Test开头&#xff0c;函数名以test_开头, 然后用pytest命令即可运行测试用例。 可以在命令行中&#xff0c;用pyte…

树莓派急速安装ubuntu;映射磁盘与储存磁盘文件;ubuntu映射整个工程;保存系统工作状态

一、用途 在使用树莓派上下载ubuntu时&#xff0c;需要一张sd卡&#xff0c;当你需要给这张卡做备份的时候&#xff0c;可以是使用磁盘映射软件&#xff0c;从而达到备份的目的 同时有一些大佬发布了ubuntu的映射文件&#xff0c;可以直接使用该文件&#xff0c;然后还原他的整…

Python学习第十九天

Django-分页 后端分页 Django提供了Paginator类来实现后端分页。Paginator类可以将一个查询集&#xff08;QuerySet&#xff09;分成多个页面&#xff0c;每个页面包含指定数量的对象。 from django.shortcuts import render, redirect, get_object_or_404 from .models impo…

Windows下安装Git客户端

① 官网地址&#xff1a;https://git-scm.com/。 ② Git的优势 大部分操作在本地完成&#xff0c;不需要联网&#xff1b;完整性保证&#xff1b;尽可能添加数据而不是删除或修改数据&#xff1b;分支操作非常快捷流畅&#xff1b;与Linux 命令全面兼容。 ③ Git的安装 从官网…

刷题练习笔记

目录 1、消失的数字 2、旋转数组 3、原地移除元素 4、删除排序数组中的重复项 1、消失的数字 oj&#xff1a;面试题 17.04. 消失的数字 - 力扣&#xff08;LeetCode&#xff09; 思路&#xff08;参考评论区&#xff09;&#xff1a; 利用异或的特性&#xff0c;ret ret …

C或C++中实现数据结构课程中的链表、数组、树和图案例

1. 双向链表&#xff08;Doubly Linked List&#xff09;-----支持双向遍历。 C实现 #include <iostream>struct Node {int data;Node* prev;Node* next; };class DoublyLinkedList { private:Node* head; public:DoublyLinkedList() : head(nullptr) {}// 在链表末尾插…

94.HarmonyOS NEXT动画系统实现教程:深入理解FuncUtils

温馨提示&#xff1a;本篇博客的详细代码已发布到 git : https://gitcode.com/nutpi/HarmonyosNext 可以下载运行哦&#xff01; HarmonyOS NEXT动画系统实现教程&#xff1a;深入理解FuncUtils 文章目录 HarmonyOS NEXT动画系统实现教程&#xff1a;深入理解FuncUtils1. 动画系…

AI日报 - 2025年3月17日

&#x1f31f; 今日概览&#xff08;60秒速览&#xff09; ▎&#x1f916; AGI突破 | GPT-o1在卡内基梅隆大学数学考试中获满分&#xff0c;展示AI数学能力新高度 成本仅5美分/题&#xff0c;推理速度不到1分钟 ▎&#x1f4bc; 商业动向 | Figure推出BotQ机器人制造设施&…

Tauri + Vite + SvelteKit + TailwindCSS + DaisyUI 跨平台开发详细配置指南(Windows)

Tauri Vite SvelteKit TailwindCSS DaisyUI 跨平台开发详细配置指南&#xff08;Windows&#xff09; 本文为博主原创文章&#xff0c;遵循 CC 4.0 BY-SA 版权协议。转载请注明出处及本声明 原文链接&#xff1a;[你的文章链接] &#x1f6e0;️ 环境准备 1. 安装核心工具…

在 macOS 上优化 Vim 用于开发

简介 这篇指南将带你通过一系列步骤&#xff0c;如何在 macOS 上优化 Vim&#xff0c;使其具备 代码补全、语法高亮、代码格式化、代码片段管理、目录树等功能。此外&#xff0c;我们还会解决在安装过程中可能遇到的常见错误。 1. 安装必备工具 在开始 Vim 配置之前&#xff…