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

列表首屏毫秒级加载与自动滚动定位方案

引用自 摸鱼wiki

场景

<template><div ref="commentsRef"><divv-for="comment in displayComments":key="comment.id":data-cell-id="comment.id"class="card">{{ comment.data }}</div></div>
</template><script lang="ts" setup>
// 假设comment有2000条数据,首次只加载200条
const comments = ref<{ id: string; data: string }[]>();
const displayComments = computed(() => comments.slice(0, 200));
</script>

说明:需要一个场景,用户首屏需要渲染200条数据,且后续如果comments列表发生变动时,页面上能够保持当前显示的条目不进行滚动。

分批加载

如果直接全量数据加载,200个节点肯定不能在1帧内渲染完毕,这样会出现较长时间的白屏。这时候可以使用时间分片的思路,在每个小时间段内渲染少量节点,提高首屏的渲染效率。

<template><div><divv-for="comment in displayComments.slice(0, loadCount)":key="comment.id">{{ comment.data }}</div></div>
</template><script lang="ts" setup>
// 假设comment有2000条数据,首次只加载200条
const commentsRef = ref();
const comments = ref<{ id: string; data: string }[]>();
const displayComments = computed(() => comments.slice(0, 200));// 首屏动态加载的数量
const loadCount = ref<number>(0);
// 如果少于20条,直接全量加载;否则每隔4ms渲染40条数据
watch(displayComments, () => {clearInterval(timer);loadCount.value = 20;if (loadCount.value < nv.length) {timer = setInterval(() => {loadCount.value = Math.min(loadCount.value + 40, nv.length);if (loadCount.value >= nv.length) {clearInterval(timer);}}, 4);}
})
</script>

滚动定位

实现在列表数据更新时自动定位到用户当前可视区域,尽可能保证可视区域的内容不因数据更新而发生剧烈抖动。

获取当前可视内容

通过浏览器提供的 IntersectionObserver API 监听列表中的元素,获取元素滚动时在可视区域内的元素,记录他们的id。

const visibleCardIds: Set<string> = new Set();
const intersection: IntersectionObserver = new IntersectionObserver((entries) => {entries.forEach((e) => {const id = e.target.getAttribute('data-cell-id');if (!id) return;if (e.isIntersecting && e.intersectionRatio > 0.91) {visibleCardIds.add(id);} else {visibleCardIds.delete(id);}});},{threshold: [0, 0.9, 1],},
);const addIntersectionObserve = () => {visibleCardIds.clear();intersection?.disconnect();nextTick(() => {const nodes = document.body.querySelectorAll('.card') || [];for (const node of nodes) {intersection?.observe(node);}});
};

滚动处理

从上一次拿到的可视区域元素中拿到在更新后的数据中仍存在的、最靠近顶部的节点,记录它的位置,并把容器节点滚动到该节点的 scrollTop 高度。

const getNearestVisibleId = () => {return displayComment.value.find(c => visibleCardIds.has(c.id));
}const silenceScroll = () => {const id = getNearestVisibleId();nextTick(() => {commentScrollTop(id);});
};const commentScrollTop = (id: string) => {const n = commentsRef.value?.querySelector(`[data-cell-id="${id}"]`,) as HTMLElement;if (n) {commentsRef.value?.scrollTo({top: n.offsetTop,behavior: 'instant',});}
};

结合起来

在数据更新后的下一帧触发滚动即可实现定位效果

<template><div><divv-for="comment in displayComments.slice(0, loadCount)":key="comment.id">{{ comment.data }}</div></div>
</template><script lang="ts" setup>
// 假设comment有2000条数据,首次只加载200条
const commentsRef = ref();
const comments = ref<{ id: string; data: string }[]>();
const displayComments = computed(() => comments.slice(0, 200));// 首屏动态加载的数量
const loadCount = ref<number>(0);
// 如果少于20条,直接全量加载;否则每隔4ms渲染40条数据
watch(displayComments, () => {clearInterval(timer);loadCount.value = 20;if (loadCount.value < nv.length) {timer = setInterval(() => {loadCount.value = Math.min(loadCount.value + 40, nv.length);if (loadCount.value >= nv.length) {clearInterval(timer);silenceScroll();addIntersectionObserve();}}, 4);} else {silenceScroll();addIntersectionObserve();}
})
</script>

定位优化

首屏加载速度上去了,也实现了自动滚动到指定的条目位置。但这时候遇到个新问题,如果列表的数据发生了变动,比如从前面插入了一条新数据,那么这时候可视范围内的数据肯定会发生改变。

如果使用批量重绘,即使本次修改只有一条数据更新,会出现页面先重新渲染,等10+ms后再滚动到指定位置,中间会出现画面的闪动。

这时候可以利用vue VDom算法的一些小Trick。先判断前后数据的差异程度,如果差异数量小于一个阈值(比如小于10),那么可以借用vue的diff算法,尽可能保留原有节点,渲染少量新节点。这时候可以大大提升页面的渲染效率,即使全量渲染也不会有性能问题。

<template><div><divv-for="comment in displayComments.slice(0, loadCount)":key="comment.id">{{ comment.data }}</div></div>
</template><script lang="ts" setup>
// 假设comment有2000条数据,首次只加载200条
const commentsRef = ref();
const comments = ref<{ id: string; data: string }[]>();
const displayComments = computed(() => comments.slice(0, 200));// 首屏动态加载的数量
const loadCount = ref<number>(0);
// 如果少于20条,直接全量加载;否则每隔4ms渲染40条数据
watch(displayComments, () => {clearInterval(timer);const nSet = new Set(nv.map((v) => v.id));const oSet = new Set(ov.map((v) => v.id));const n = new Set([...nSet].filter((x) => !oSet.has(x)));if (n.size <= 10) {loadCount.value = nv.length;silenceScroll();addIntersectionObserve();} else {loadCount.value = 20;if (loadCount.value < nv.length) {timer = setInterval(() => {loadCount.value = Math.min(loadCount.value + 40, nv.length);if (loadCount.value >= nv.length) {clearInterval(timer);silenceScroll();addIntersectionObserve();}}, 4);} else {silenceScroll();addIntersectionObserve();}}
})
</script>

相关文章:

列表首屏毫秒级加载与自动滚动定位方案

引用自 摸鱼wiki 场景 <template><div ref"commentsRef"><divv-for"comment in displayComments":key"comment.id":data-cell-id"comment.id"class"card">{{ comment.data }}</div></div> &…...

小区物业业主管理信息系统设计的设计与实现(论文+源码)_kaic

摘 要 随着互联网的发展&#xff0c;网络技术的发展变得极其重要&#xff0c;所以依靠计算机处理业务成为了一种社会普遍的现状。管理方式也自然而然的向着现代化技术方向而改变&#xff0c;所以纯人工管理方式在越来越完善的现代化管理技术的比较之下也就显得过于繁琐&#x…...

Fortran 微分方程求解 --ODEPACK

最近涉及到使用Fortran对微分方程求解&#xff0c;我们知道MATLAB已有内置的函数&#xff0c;比如ode家族&#xff0c;ode15s&#xff0c;对应着不同的求解办法。通过查看odepack的官方文档&#xff0c;我尝试使用了dlsode求解刚性和非刚性常微分方程组。 首先是github网址&am…...

8路光栅尺磁栅尺编码器或16路高速DI脉冲信号转Modbus TCP网络模块 YL99-RJ45

特点&#xff1a; ● 光栅尺磁栅尺解码转换成标准Modbus TCP协议 ● 高速光栅尺磁栅尺4倍频计数&#xff0c;频率可达5MHz ● 模块可以输出5V的电源给光栅尺或传感器供电 ● 支持8个光栅尺同时计数&#xff0c;可识别正反转 ● 可以设置作为16路独立DI高速计数器 ● 可网…...

【Python】函数

None类型 思考&#xff1a;若函数没有使用return语句返回数据&#xff0c;那么函数有返回值吗&#xff1f; 答&#xff1a;实际上是有的&#xff0c;Python中有一个特殊的字面量None&#xff0c;其类型是<class ‘NoneType’>&#xff0c;无返回值的函数&#xff0c;实…...

centos安装MySQL 解压版完整教程(按步骤傻瓜式安装

一、卸载系统自带的 Mariadb 查看&#xff1a; rpm -qa|grep mariadb 卸载&#xff1a; rpm -e --nodeps mariadb-libs-5.5.68-1.el7.x86_64 二、卸载 etc 目录下的 my.cnf 文件 rm -rf /etc/my.cnf 三、检查MySQL是否存在 有则先删除 #卸载mysql服务以及删除所有mysql目录 #没…...

【后端速成 Vue】第一个 Vue 程序

1、为什么要学习 Vue&#xff1f; 为什么使用 Vue? 回想之前&#xff0c;前后端交互的时候&#xff0c;前端收到后端响应的数据&#xff0c;接着将数据渲染到页面上&#xff0c;之前使用的是 JavaScript 或者 基于 JavaScript 的 Jquery&#xff0c;但是这两个用起来还是不太…...

Macbook pro M1 安装Ubuntu教程

先讲下心路历程 由于版主最近刚切换到Mac&#xff0c;所以在安装的时候一上手就选择了virutalbox&#xff0c;结果报错“The installer has detected an unsupported architecture. VirtualBox only runs on the amd64 architecture.” 后来去Reddit论坛上一看&#xff0c;才知…...

前端console.log打印内容与后端请求返回数据不一致

后端传值num0 前端打印num1 ,如图&#xff0c;console.log后台显示的数据与展开后不一致 造成该问题原因是深拷贝与浅拷贝的问题。 var obj JSON.parse(JSON.stringify(res)) 修改后打印 正常...

SQL入门:多表查询

SQL&#xff0c;或者说结构化查询语言(Structured Query Language)&#xff0c;是用于管理和操作关系型数据库的标准语言。在本篇文章中&#xff0c;我们将重点介绍SQL中的多表查询&#xff0c;这是一种强大的工具&#xff0c;可以帮助我们从多个相关的表格中获取数据。 数据库…...

【C++】进一步认识模板

&#x1f3d6;️作者&#xff1a;malloc不出对象 ⛺专栏&#xff1a;C的学习之路 &#x1f466;个人简介&#xff1a;一名双非本科院校大二在读的科班编程菜鸟&#xff0c;努力编程只为赶上各位大佬的步伐&#x1f648;&#x1f648; 目录 前言一、非类型模板参数二、模板的特…...

Mysql Oracle 区别

1. oracle select *, id需要在星号前加别名&#xff0c;mysql则不需要 mysql语法&#xff1a; select *, id from xin_student_t;oracle语法&#xff1a; select st.*, st.id from xin_student_t st;2. oracle表定义了别名&#xff0c;在查询时可以不用别名指定字段&#xf…...

华为OD-第K长的连续字母字符串长度

题目描述 给定一个字符串&#xff0c;只包含大写字母&#xff0c;求在包含同一字母的子串中&#xff0c;长度第 k 长的子串的长度&#xff0c;相同字母只取最长的那个子串。 代码实现 # coding:utf-8 # 第K长的连续字母字符串长度 # https://www.nowcoder.com/discuss/353150…...

【编程题】有效三角形的个数

文章目录 一、题目二、算法讲解三、题目链接四、补充 一、题目 给定一个包含非负整数的数组 nums &#xff0c;返回其中可以组成三角形三条边的三元组个数。 示例1&#xff1a; 输入: nums [2,2,3,4] 输出: 3 **解释:**有效的组合是: 2,3,4 (使用第一个 2) 2,3,4 (使用第二个 …...

【mysql是怎样运行的】-EXPLAIN详解

文章目录 1.基本语法2. EXPLAIN各列作用1. table2. id3. select_type4. partitions5. type 1.基本语法 EXPLAIN SELECT select_options #或者 DESCRIBE SELECT select_optionsEXPLAIN 语句输出的各个列的作用如下&#xff1a; 列名描述id在一个大的查询语句中每个SELECT关键…...

数据结构例题代码及其讲解-链表

链表 单链表的结构体定义及其初始化。 typedef struct LNode {int data;struct LNode* next; }LNode, *LinkList;①强调结点 LNode *p; ②强调链表 LinkList p; //初始化 LNode* initList() {//定义头结点LNode* L (LNode*)malloc(sizeof(LNode));L->next NULL;return …...

[Open-source tool] 可搭配PHP和SQL的表單開源工具_Form tools(1):簡介和建置

Form tools是一套可搭配PHP和SQL的表單開源工具&#xff0c;可讓開發者靈活運用&#xff0c;同時其有數個表單模板和應用模組供挑選&#xff0c;方便且彈性。Form tools已開發超過20年&#xff0c;為不同領域的需求者或開發者提供一個自由和開放的平台&#xff0c;使他們可建構…...

移动数据业务价值链的整合

3G 时代移动数据业务开发体系的建立和发展&#xff0c;要求运营商从封闭、统一的业 务形态、单一提供业务&#xff0c;向开放的、个性化多元化的业务体系以及多方合作参与提 供业务的方向发展&#xff0c;不可避免的使通信价值链不断延长和升级&#xff0c;内容提供商、服务 …...

合并两个链表

题目描述 将两个升序链表合并为一个新的 升序 链表并返回。新链表是通过拼接给定的两个链表的所有节点组成的。 比如以下例子&#xff1a; 题目接口&#xff1a; /*** Definition for singly-linked list.* struct ListNode {* int val;* ListNode *next;* ListN…...

测试框架pytest教程(9)跳过测试skip和xfail

skip无条件跳过 使用装饰器 pytest.mark.skip(reason"no way of currently testing this") def test_example(faker):print("nihao")print(faker.words()) 方法内部调用 满足条件时跳过 def test_example():a1if a>0:pytest.skip("unsupported …...

未来机器人的大脑:如何用神经网络模拟器实现更智能的决策?

编辑&#xff1a;陈萍萍的公主一点人工一点智能 未来机器人的大脑&#xff1a;如何用神经网络模拟器实现更智能的决策&#xff1f;RWM通过双自回归机制有效解决了复合误差、部分可观测性和随机动力学等关键挑战&#xff0c;在不依赖领域特定归纳偏见的条件下实现了卓越的预测准…...

挑战杯推荐项目

“人工智能”创意赛 - 智能艺术创作助手&#xff1a;借助大模型技术&#xff0c;开发能根据用户输入的主题、风格等要求&#xff0c;生成绘画、音乐、文学作品等多种形式艺术创作灵感或初稿的应用&#xff0c;帮助艺术家和创意爱好者激发创意、提高创作效率。 ​ - 个性化梦境…...

ubuntu搭建nfs服务centos挂载访问

在Ubuntu上设置NFS服务器 在Ubuntu上&#xff0c;你可以使用apt包管理器来安装NFS服务器。打开终端并运行&#xff1a; sudo apt update sudo apt install nfs-kernel-server创建共享目录 创建一个目录用于共享&#xff0c;例如/shared&#xff1a; sudo mkdir /shared sud…...

从WWDC看苹果产品发展的规律

WWDC 是苹果公司一年一度面向全球开发者的盛会&#xff0c;其主题演讲展现了苹果在产品设计、技术路线、用户体验和生态系统构建上的核心理念与演进脉络。我们借助 ChatGPT Deep Research 工具&#xff0c;对过去十年 WWDC 主题演讲内容进行了系统化分析&#xff0c;形成了这份…...

RNN避坑指南:从数学推导到LSTM/GRU工业级部署实战流程

本文较长&#xff0c;建议点赞收藏&#xff0c;以免遗失。更多AI大模型应用开发学习视频及资料&#xff0c;尽在聚客AI学院。 本文全面剖析RNN核心原理&#xff0c;深入讲解梯度消失/爆炸问题&#xff0c;并通过LSTM/GRU结构实现解决方案&#xff0c;提供时间序列预测和文本生成…...

使用 Streamlit 构建支持主流大模型与 Ollama 的轻量级统一平台

🎯 使用 Streamlit 构建支持主流大模型与 Ollama 的轻量级统一平台 📌 项目背景 随着大语言模型(LLM)的广泛应用,开发者常面临多个挑战: 各大模型(OpenAI、Claude、Gemini、Ollama)接口风格不统一;缺乏一个统一平台进行模型调用与测试;本地模型 Ollama 的集成与前…...

均衡后的SNRSINR

本文主要摘自参考文献中的前两篇&#xff0c;相关文献中经常会出现MIMO检测后的SINR不过一直没有找到相关数学推到过程&#xff0c;其中文献[1]中给出了相关原理在此仅做记录。 1. 系统模型 复信道模型 n t n_t nt​ 根发送天线&#xff0c; n r n_r nr​ 根接收天线的 MIMO 系…...

Android第十三次面试总结(四大 组件基础)

Activity生命周期和四大启动模式详解 一、Activity 生命周期 Activity 的生命周期由一系列回调方法组成&#xff0c;用于管理其创建、可见性、焦点和销毁过程。以下是核心方法及其调用时机&#xff1a; ​onCreate()​​ ​调用时机​&#xff1a;Activity 首次创建时调用。​…...

免费PDF转图片工具

免费PDF转图片工具 一款简单易用的PDF转图片工具&#xff0c;可以将PDF文件快速转换为高质量PNG图片。无需安装复杂的软件&#xff0c;也不需要在线上传文件&#xff0c;保护您的隐私。 工具截图 主要特点 &#x1f680; 快速转换&#xff1a;本地转换&#xff0c;无需等待上…...

iview框架主题色的应用

1.下载 less要使用3.0.0以下的版本 npm install less2.7.3 npm install less-loader4.0.52./src/config/theme.js文件 module.exports {yellow: {theme-color: #FDCE04},blue: {theme-color: #547CE7} }在sass中使用theme配置的颜色主题&#xff0c;无需引入&#xff0c;直接可…...