超大表格组件滚动渲染优化

引用自 摸鱼wiki

背景

业务中需要渲染一个最多有100+列的表格,由于表格使用原生dom实现,因此会出现同屏有近1000个单元格同时绘制,在快速滑动时页面会产生卡顿,影响用户体验。

方案

如下图所示,由于用户显示屏区域有限,实际上并不能同时看见超大表格的所有列,因此可以考虑虚拟滚动的设计思路,仅渲染用户可视区域内的单元格及其内容,区域外的尽可能减少渲染。

于是可以考虑把表格拆分成三个区域:

  1. 可视区域:即图中绿色标注单元格,这部分单元格用户可见,必须完整渲染
  2. 缓冲区域:即图中蓝色标注单元格,这部分单元格用户不可见,但在用户左右快速滑动时有可能可见,因此可以进行预渲染
  3. 隐藏区域:即图中红色标注单元格,这部分单元格用户不可见,且在一般情况下会一直保持不可见状态,因此可以省略其渲染

在这里插入图片描述
针对上述三种单元格类型,可以有对应三种渲染策略:

  1. 可视单元格:正常渲染
  2. 缓冲单元格:正常渲染
  3. 隐藏单元格:相邻单元格合并成一个大的空白div(图中的紫色单元格),拼接在两端,由于是一个空白div,因此渲染性能损耗极低

通过上述方法可以看到,原本一个12列的表格,在渲染的时候仅会渲染10列,包含6列可视区+2列缓冲区 + 2列填充区(消耗几乎为0),理论渲染效率提高15%-30%。由于用户视口大小是固定的,因此无论表格有多少列,同时都只会有10列单元格在实时渲染,列数越多则性能提升越大。

实施

1. 使用IntersectionObserver监听表头

new IntersectionObserver((entries) => {let isChange = false;entries.forEach((e) => {const columnKey = e.target.getAttribute('column-key');if (!columnKey) return;if (e.intersectionRatio > 0.0) {this.visibleHeader.add(columnKey);isChange = true;} else {isChange = true;this.visibleHeader.delete(columnKey);}});if (isChange) {this.visibleHeaderChange.value = Date.now();}},{root,threshold: [0, 1],},
);

2. 计算前后空白宽度拼接行渲染数据

const displayCols = []
// 可视区域
for (const col of this.visibleHeader) {displayCols.push(col);
}
// 缓冲区域
displayCols.unshift(prevCacheCol);
displayCols.push(nextCacheCol);
// 不可见区域
displayCols.unshift(prevEmptyCol);
displayCols.push(nextEmptyCol);

3. 使用ResizeObserver监听行高变化

由于横向虚拟,行高仅与可视区+缓冲区的单元格相关,来回滚动时可能会导致行高发生变化导致抖动,因此需要给行元素添加resize监听,获取最大行高并记录,减少横向滚动的抖动

new ResizeObserver((entries) => {entries.forEach((entry) => {const rowKey = entry.target.getAttribute('data-id');if (!rowKey) return;const { height } = entry.contentRect;row.height = max(height, row.height)});
});

效果

设备:MacBook M1Pro + 120Hz高刷屏
数据量:单表90列数据

改造前:
在这里插入图片描述
改造后:
在这里插入图片描述

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

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

相关文章

ElementUI之首页导航及左侧菜单(模拟实现)

目录 ​编辑 前言 一、mockjs简介 1. 什么是mockjs 2. mockjs的用途 3. 运用mockjs的优势 二、安装与配置mockjs 1. 安装mockjs 2. 引入mockjs 2.1 dev.env.js 2.2 prod.env.js 2.3 main.js 三、mockjs的使用 1. 将资源中的mock文件夹复制到src目录下 2. 点击登…

蓝桥杯 题库 简单 每日十题 day11

01 质数 质数 题目描述 给定一个正整数N,请你输出N以内(不包含N)的质数以及质数的个数。 输入描述 输入一行,包含一个正整数N。1≤N≤10^3 输出描述 共两行。 第1行包含若干个素数,每两个素数之间用一个空格隔开&…

【切片】基础不扎实引发的问题

本次文章主要是来聊聊关于切片传值需要注意的问题,如果不小心,则很容易引发线上问题,如果不够理解,可能会出现奇奇怪怪的现象 问题情况: 小 A 负责一个模块功能的实现,在调试代码的时候可能不仔细&#x…

OpenAI 更新 ChatGPT:支持图片和语音输入【附点评】

一、消息正文 9月25日消息,近日OpenAI宣布其对话AI系统ChatGPT进行升级,添加了语音输入和图像处理两个新功能。据OpenAI透露,这些新功能将在未来两周内面向ChatGPT Plus付费用户推出,免费用户也将很快可以使用这些新功能。这标志着ChatGPT继续朝着多模态交互的方向发展,为用户提…

Lnmp架构之mysql数据库实战2

4、mysql组复制集群 一主多从的请求通常是读的请求高于写 ,但是如果写的请求很高,要求每个节点都可以进行读写,这时分布式必须通过(多组模式)集群的方式进行横向扩容。 组复制对节点的数据一致性要求非常高&#xff…

EcmaScript标准-导入与导出-js

ECMAScript是一种由Ecma国际(前身为欧洲计算机制造商协会,European Computer Manufacturers Association)通过ECMA-262标准化的脚本程序设计语言。这种语言在万维网上应用广泛,它往往被称为JavaScript或JScript,所以它…

Cannot download sources

问题 Swagger的相关包,没法看到注释;源码也下载不了,会报下面的错误。 解决办法是,通过maven,重新下载jar包。 报错 Cannot download sources Sources not found for: io.swagger.core.v3:swagger-annotations:2.2.…

主从复制是怎么实现的?

单机模式的缺点 Redis虽然有持久化技术保证Redis奔溃后重启可以恢复数据,但是,单机模式下还是存在两方面问题。一方面Redis一旦宕机,数据恢复需要一定的时间,这段时间内,都不能接收和处理请求;另一方面&am…

【教程】Ubuntu自动查看有哪些用户名与密码相同的账户,并统一修改密码

转载请注明出处:小锋学长生活大爆炸[xfxuezhagn.cn] 目录 背景说明 开始操作 修改密码 背景说明 有些用户为了图方便或者初始创建用户默认设置等原因,会将密码设置为与用户名相同,但这就使得非常不安全。甚至如果该用户具有sudo权限&#…

云原生Kubernetes:Pod控制器

目录 一、理论 1.Pod控制器 2.Deployment 控制器 3.SatefulSet 控制器 4.DaemonSet 控制器 5.Job 控制器 6.CronJob 控制器 二、实验 1.Deployment 控制器 2.SatefulSet 控制器 3.DaemonSet 控制器 4.Job 控制器 5.CronJob 控制器 三、问题 1. showmount -e 报错…

HTML5福利篇--使用Canvas画图

目录 一.Canvas元素 1.Canvas元素定义 2.使用JavaScript获取页面中的Canvas对象 二.绘制图形 1.绘制直线 2.绘制矩形 (1)rect() (2)strokeRect() (3)fillRect()和clearRect()函数 3.绘制圆弧 4.…

layui 树状控件tree优化

先上效果图: 我选的组件是这个: 动态渲染完后,分别在窗体加载完成,节点点击事件分别加入js: //侧边栏图标替换//layui-icon-subtraction$(function () {$(".layui-icon-file").addClass("backs&quo…