当前位置: 首页 > news >正文

IntersectionObserver实现小程序长列表优化

IntersectionObserver实现小程序长列表优化

关于 IntersectionObserver

思路

这里以一屏数据为单位【一个分页的10条数据,最好大于视口高度】,
监听每一屏数据和视口的相交比例,即用户能不能看到它
只将可视范围的数据渲染到页面上,其余的使用空白高度占位符代替,

可视范围可扩大到当前可视范围的上下两倍到三倍,减少滚动时留白现象

实现

大多数都是监听 scroll,结合防抖函数,来控制数据的渲染

这里我们使用小程序自带api,IntersectionObserver 来实现

小程序 IntersectionObserver

通过 IntersectionObserver 来监听可视范围内的元素,当元素进入可视范围时,将对应的数据渲染到页面上。

其返回的 intersectionRatio 为相交比例,大于0 说明进入可视范围,渲染真实数据,否则,渲染高度占位符

第一步

首先需要两个数组,一个用来存放真实数据,一个用来存放高度占位符数据

将原来的 storeList,改为二维数组,以一屏为单位进行分割

// Taro// wholePageHeight和wholeStoreList不用放在state中,页面销毁时记得释放// 高度占位符数据
let wholePageHeight = [];
// 真实数据
let wholeStoreList = [];state = {// 当前页码pageCurrent: 0,// 渲染的数据storeList: [],// 高度占位符数据storeListPlaceholder: [],// ...
}

第二步

获取界面上的节点信息

保存所有的列表数据以及页面高度数据

每次数据请求完成,将列表数据push到 wholeStoreList 中,
然后将当前这一屏的数据放到对应页面的 pageCurrentstoreList 中,
setState后, 计算这一屏的高度,然后将其存放到 wholePageHeight

// Taro// 分页获取列表数据
getList = async () => {// 接口请求数据const { pageCurrent } = this.state;const records = await api.getList({pageCurrent});// 分页累加pageCurrent++// 保存所有的列表数据wholeStoreList.push(records);// 设置当前这一屏的数据storeList[pageCurrent] = records;this.setState({storeList,pageCurrent,}, () => {// 数据setState后,获取当前页的高度this.getPageHeight();});
}// 计算当前页的高度
getPageHeight = () => {const {pageCurrent,} = this.state;// 返回一个 SelectorQuery 对象实例const query = Taro.createSelectorQuery();// 查询节点信息的对象query// 在当前页面下选择第一个匹配选择器 selector 的节点。返回一个 NodesRef 对象实例,可以用于获取节点信息。// 类似于 CSS 的选择器.select(`#storePage${pageCurrent}`)// 添加节点的布局位置的查询请求,SelectorQuery.exec 方法后,节点信息会在 callback 中返回.boundingClientRect()// 执行所有的请求。请求结果按请求次序构成数组,在callback的第一个参数中返回。.exec(res => {if (res && res[0].height) {/***   rect.id      // 节点的ID*   rect.dataset // 节点的dataset*   rect.left    // 节点的左边界坐标*   rect.right   // 节点的右边界坐标*   rect.top     // 节点的上边界坐标*   rect.bottom  // 节点的下边界坐标*   rect.width   // 节点的宽度*   rect.height  // 节点的高度*/// 保存当前页的高度wholePageHeight.push(res[0].height);// 监听当前页的节点this.observePage(pageCurrent);}});
}

第三步

针对每一屏都去添加监听,判断是否需要渲染真实数据,还是高度占位符

这里通过 IntersectionObserver 来监听,当元素进入可视范围时,将对应的数据渲染到页面上。

:::tip 小细节

如果只控制一屏的显示,那么当用户快速滑动上下屏时,
会出现一屏的数据还没渲染完,就已经滚动到下一屏了,导致白屏出现

所以:需要设置 relativeToViewport top 和 bottom 参数,顶部和底部的边界,进入上下三个屏幕高度就开始渲染

:::

// TaroobservePage = () => {const {storeList,} = this.state;const windowHeight = Taro.systemInfo.windowHeight;// WXML节点布局相交状态// 创建 IntersectionObserver 对象,用于推断某些节点是否可以被用户看见、有多大比例可以被用户看见。const observerObj = Taro.createIntersectionObserver(this)// 指定页面显示区域作为参照区域之一.relativeToViewport({// 设置顶部和底部的边界,进入上下三个屏幕高度就开始渲染top: 3 * windowHeight, bottom: 3 * windowHeight});// 返回IntersectionObserver对象实例,用于推断某些节点是否可以被用户看见、有多大比例可以被用户看见observerObj.observe(`#storePage${pageIndex}`, (res) => {/***   res.id // 目标节点 id*   res.dataset // 目标节点 dataset*   res.intersectionRatio // 相交区域占目标节点的布局区域的比例*   res.intersectionRect // 相交区域*   res.intersectionRect.left // 相交区域的左边界坐标*   res.intersectionRect.top // 相交区域的上边界坐标*   res.intersectionRect.width // 相交区域的宽度*   res.intersectionRect.height // 相交区域的高度*/// < 0,未相交,使用高度占位符if (res.intersectionRatio <= 0) {storeList[pageIndex] = {height: wholePageHeight[pageIndex]}// > 0,相交,使用真实数据} else {storeList[pageIndex] = wholeStoreList[pageIndex];}this.setState({storeList});});
}

第四步

在页面上渲染数据,如果当前这一屏有数据,就渲染真实数据,否则渲染高度高度占位符


// Taro// 列表渲染
renderStoreList = () => {const {storeList,} = this.state;return (storeList.map((storePage, index) => (<View className='store-page' key={index} id={'storePage' + index}>{// 是否存在当前页的数据storePage && storePage.length ?<View className='store-list'>{storePage.map((storeItem) => (<StoreInfo item={storeItem} key={storeItem.id} />))}</View>:// 占位组件 ---<PlaceWrap customStyle={{height: storePage.height + 'px'}} />}</View>)))
};

到此,一个长列表的优化就OK了

IntersectionObserver

看了下 caniuse ,发现除了IE,兼容性也还行,那web端也可以尝试下的

在这里插入图片描述

相关文章:

IntersectionObserver实现小程序长列表优化

IntersectionObserver实现小程序长列表优化 关于 IntersectionObserver 思路 这里以一屏数据为单位【一个分页的10条数据&#xff0c;最好大于视口高度】&#xff0c; 监听每一屏数据和视口的相交比例&#xff0c;即用户能不能看到它 只将可视范围的数据渲染到页面上&#x…...

Nginx动静分离、资源压缩、负载均衡、黑白名单、防盗链等实战

一、前言 Nginx是目前负载均衡技术中的主流方案&#xff0c;几乎绝大部分项目都会使用它&#xff0c;Nginx是一个轻量级的高性能HTTP反向代理服务器&#xff0c;同时它也是一个通用类型的代理服务器&#xff0c;支持绝大部分协议&#xff0c;如TCP、UDP、SMTP、HTTPS等。 二、…...

Rust之枚举与模式匹配

枚举类型&#xff0c;简称枚举&#xff0c;允许列举所有可能的值来定义一个类型。 1、定义枚举&#xff1a; 枚举类型&#xff1a;已知所有可能的值&#xff0c;并且所有值的出现是互斥的&#xff0c;即每次只能取一种可能的值&#xff0c;才使用枚举类型。 示例&#xff1a;…...

nfs服务器的描述,搭建和使用

前言 这是我在这个网站整理的笔记&#xff0c;关注我&#xff0c;接下来还会持续更新。 作者&#xff1a;RodmaChen nfs服务器的描述&#xff0c;搭建和使用 NFS概述工作原理优缺点 nfs服务器搭建服务端客户端 NFS概述 NFS&#xff08;Network File System&#xff09;是一种基…...

libuv库学习笔记-filesystem

Filesystem 简单的文件读写是通过uv_fs_*函数族和与之相关的uv_fs_t结构体完成的。 note libuv 提供的文件操作和 socket operations 并不相同。套接字操作使用了操作系统本身提供了非阻塞操作&#xff0c;而文件操作内部使用了阻塞函数&#xff0c;但是 libuv 是在线程池中调…...

记录vue的一些踩坑日记

记录vue的一些踩坑日记 安装Jq npm install jquery --save vue列表跳转到详情页&#xff0c;再返回列表的时候不刷新页面并且保持原位置不变&#xff1b; 解决&#xff1a;使用keepAlive 在需要被缓存的页面的路由中添加&#xff1a;keepAlive: true, {path: /viewExamine,nam…...

Mybatis学习笔记

Mybatis 文章目录 Mybatis搭建环境创建Maven工程将数据库中的表转换为对应的实体类配置文件核心配置文件mybatis-config.xml创建Mapper接口映射文件xxxMapper.xmllog4j日志功能 Mybatis操纵数据库示例及要点说明获取参数的两种方式${}#{} 各种类型的参数处理单个字面量参数多个…...

网络编程(11):三次握手和四次挥手部分细节(后续补充)

关于listen 服务器如果不listen&#xff0c;TCP协议栈就无法从CLOSED状态变成LISTEN状态&#xff0c;客户端发起连接&#xff0c;TCP协议栈会直接返回RST报文&#xff0c;从而导致客户端连接失败 关于accept accept发送在三次握手完成之后&#xff0c;从全连接队列中取出一个节…...

MySQL学习笔记 ------ 子查询

#进阶7&#xff1a;子查询 /* 含义&#xff1a; 出现在其他语句中的select语句&#xff0c;称为子查询或内查询 外部的查询语句&#xff0c;称为主查询或外查询 分类&#xff1a; 按子查询出现的位置&#xff1a; select后面&#xff1a; 仅仅支持标量子查询 …...

自然语言处理应用程序设计

原文地址&#xff1a;https://zhanghan.xyz/posts/22426/ 文章目录 一、摘要二、数据集三、相关环境四、功能展示1.系统主界面2.中文分词3.命名实体识别4.文本分类5.文本聚类6.其他界面 五、源码链接 一、摘要 将自然语言处理课程设计中实现的模型集成到自然语言处理应用程序…...

LeetCode 436. Find Right Interval【排序,二分;双指针,莫队】中等

本文属于「征服LeetCode」系列文章之一&#xff0c;这一系列正式开始于2021/08/12。由于LeetCode上部分题目有锁&#xff0c;本系列将至少持续到刷完所有无锁题之日为止&#xff1b;由于LeetCode还在不断地创建新题&#xff0c;本系列的终止日期可能是永远。在这一系列刷题文章…...

正则表达式 —— Sed

Sed Sed 类似于vim就是一个文本编辑器&#xff0c;按行来进行编辑和排序 Sed的原理&#xff1a;读取&#xff0c;执行&#xff0c;显示 读取&#xff1a;读取文本内容之后&#xff0c;读取到的内容存放到临时的缓冲区—模式空间 执行&#xff1a;在模式空间&#xff0c;根据…...

TypeScript中数组,元组 和 枚举类型

数组 方式一 let arr: number[] [1, 2, 3, 4]方式二&#xff0c;使用泛型定义 let arr: Array<number> [1, 2, 3, 4]方式三&#xff0c;使用any let arr: any[] [12, string, true] console.log(arr[1]) // string元组 可以定义不同类型定义类型顺序需保持一直 …...

MyBatis-Plus-Join 多表查询的扩展

文章目录 网站使用方法安装使用Lambda形式用法&#xff08;MPJLambdaWrapper&#xff09;简单的连表查询一对多查询 网站 官方网站&#xff1a;https://mybatisplusjoin.com/Github地址&#xff1a;https://github.com/yulichang/mybatis-plus-joinGitee地址&#xff1a;https…...

认清现实重新理解游戏的本质

认清现实重新理解游戏的本质 OVERVIEW 认清现实重新理解游戏的本质现实两条小路的启发四个动机1.当前的学习任务或工作任务太艰巨2.完美主义3.对未来太过于自信/无知4.大脑小看未来的收益 四个方法1.让未来的收益足够巨大2.让未来的收益感觉就在眼前3.玩游戏有恶劣的结果4.玩游…...

LeetCode 2050. Parallel Courses III【记忆化搜索,动态规划,拓扑排序】困难

本文属于「征服LeetCode」系列文章之一&#xff0c;这一系列正式开始于2021/08/12。由于LeetCode上部分题目有锁&#xff0c;本系列将至少持续到刷完所有无锁题之日为止&#xff1b;由于LeetCode还在不断地创建新题&#xff0c;本系列的终止日期可能是永远。在这一系列刷题文章…...

ETHERNET/IP转RS485/RS232网关什么是EtherNet/IP?

网络数据传输遇到的协议不同、数据互通麻烦等问题&#xff0c;一直困扰着大家。然而&#xff0c;现在有一种神器——捷米JM-EIP-RS485/232&#xff0c;它将ETHERNET/IP网络和RS485/RS232总线连接在一起&#xff0c;让数据传输更加便捷高效。 那么&#xff0c;它是如何实现这一功…...

使用node内置test runner,和 Jest say 拜拜

参考 https://nodejs.org/dist/latest-v20.x/docs/api/test.html#test-runner 在之前&#xff0c;我们写单元测试&#xff0c;必须安装第三方依赖包&#xff0c;而从node 20.0.0 版本之后&#xff0c;可以告别繁琐的第三方依赖包啦&#xff0c;可直接使用node的内置test runner…...

《面试1v1》Kafka的架构设计是什么样子

&#x1f345; 作者简介&#xff1a;王哥&#xff0c;CSDN2022博客总榜Top100&#x1f3c6;、博客专家&#x1f4aa; &#x1f345; 技术交流&#xff1a;定期更新Java硬核干货&#xff0c;不定期送书活动 &#x1f345; 王哥多年工作总结&#xff1a;Java学习路线总结&#xf…...

比较常见CPU的区别:Intel、ARM、AMD

一、开发公司不同 1、Intel&#xff1a;是英特尔公司开发的中央处理器&#xff0c;有移动、台式、服务器三个系列。 2、ARM&#xff1a;是英国Acorn有限公司设计的低功耗成本的第一款RISC微处理器。 3、AMD&#xff1a;由AMD公司生产的处理器。 二、技术不同 1、Intel&…...

eNSP-Cloud(实现本地电脑与eNSP内设备之间通信)

说明&#xff1a; 想象一下&#xff0c;你正在用eNSP搭建一个虚拟的网络世界&#xff0c;里面有虚拟的路由器、交换机、电脑&#xff08;PC&#xff09;等等。这些设备都在你的电脑里面“运行”&#xff0c;它们之间可以互相通信&#xff0c;就像一个封闭的小王国。 但是&#…...

CVPR 2025 MIMO: 支持视觉指代和像素grounding 的医学视觉语言模型

CVPR 2025 | MIMO&#xff1a;支持视觉指代和像素对齐的医学视觉语言模型 论文信息 标题&#xff1a;MIMO: A medical vision language model with visual referring multimodal input and pixel grounding multimodal output作者&#xff1a;Yanyuan Chen, Dexuan Xu, Yu Hu…...

云计算——弹性云计算器(ECS)

弹性云服务器&#xff1a;ECS 概述 云计算重构了ICT系统&#xff0c;云计算平台厂商推出使得厂家能够主要关注应用管理而非平台管理的云平台&#xff0c;包含如下主要概念。 ECS&#xff08;Elastic Cloud Server&#xff09;&#xff1a;即弹性云服务器&#xff0c;是云计算…...

镜像里切换为普通用户

如果你登录远程虚拟机默认就是 root 用户&#xff0c;但你不希望用 root 权限运行 ns-3&#xff08;这是对的&#xff0c;ns3 工具会拒绝 root&#xff09;&#xff0c;你可以按以下方法创建一个 非 root 用户账号 并切换到它运行 ns-3。 一次性解决方案&#xff1a;创建非 roo…...

Linux云原生安全:零信任架构与机密计算

Linux云原生安全&#xff1a;零信任架构与机密计算 构建坚不可摧的云原生防御体系 引言&#xff1a;云原生安全的范式革命 随着云原生技术的普及&#xff0c;安全边界正在从传统的网络边界向工作负载内部转移。Gartner预测&#xff0c;到2025年&#xff0c;零信任架构将成为超…...

Psychopy音频的使用

Psychopy音频的使用 本文主要解决以下问题&#xff1a; 指定音频引擎与设备&#xff1b;播放音频文件 本文所使用的环境&#xff1a; Python3.10 numpy2.2.6 psychopy2025.1.1 psychtoolbox3.0.19.14 一、音频配置 Psychopy文档链接为Sound - for audio playback — Psy…...

Spring AI 入门:Java 开发者的生成式 AI 实践之路

一、Spring AI 简介 在人工智能技术快速迭代的今天&#xff0c;Spring AI 作为 Spring 生态系统的新生力量&#xff0c;正在成为 Java 开发者拥抱生成式 AI 的最佳选择。该框架通过模块化设计实现了与主流 AI 服务&#xff08;如 OpenAI、Anthropic&#xff09;的无缝对接&…...

前端开发面试题总结-JavaScript篇(一)

文章目录 JavaScript高频问答一、作用域与闭包1.什么是闭包&#xff08;Closure&#xff09;&#xff1f;闭包有什么应用场景和潜在问题&#xff1f;2.解释 JavaScript 的作用域链&#xff08;Scope Chain&#xff09; 二、原型与继承3.原型链是什么&#xff1f;如何实现继承&a…...

Linux离线(zip方式)安装docker

目录 基础信息操作系统信息docker信息 安装实例安装步骤示例 遇到的问题问题1&#xff1a;修改默认工作路径启动失败问题2 找不到对应组 基础信息 操作系统信息 OS版本&#xff1a;CentOS 7 64位 内核版本&#xff1a;3.10.0 相关命令&#xff1a; uname -rcat /etc/os-rele…...

现有的 Redis 分布式锁库(如 Redisson)提供了哪些便利?

现有的 Redis 分布式锁库&#xff08;如 Redisson&#xff09;相比于开发者自己基于 Redis 命令&#xff08;如 SETNX, EXPIRE, DEL&#xff09;手动实现分布式锁&#xff0c;提供了巨大的便利性和健壮性。主要体现在以下几个方面&#xff1a; 原子性保证 (Atomicity)&#xff…...