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

vue+element-ui el-table组件二次封装实现虚拟滚动,解决数据量大渲染DOM过多而卡顿问题

一、此功能已集成到TTable组件中

二、最终效果

在这里插入图片描述

三、需求

某些页面不做分页时,当数据过多,会导致页面卡顿,甚至卡死

四、虚拟滚动

一、固定一个可视区域的大小并且其大小是不变的,那么要做到性能最大化就需要尽量少地渲染 DOM 元素,而这个最小值也就是可视范围内需要展示的内容,而可视区域之外的元素均可以不做渲染。
二、如何计算可视区域内需要渲染的元素,我们通过如下几步来实现虚拟滚动:

1、每一行的高度需要相同,方便计算。
2、需要知道渲染的数据量(数组长度),可基于总量和每个元素的高度计算出容器整体的所需高度,这样就可以伪造一个真实的滚动条。
3、获取可视区域的高度。
4、在滚动事件触发后,滚动条的距顶距离即这个数据量中的偏移量,再根据可视区域本身的高度,算出本次偏移量,这样就得到了需要渲染的具体数据

五、具体实现(源码)

<template><div class="t-table" id="t_table"><el-tableref="el-table":data="tableData":class="{cursor: isCopy,row_sort: isRowSort,highlightCurrentRow: highlightCurrentRow,radioStyle: table.firstColumn && table.firstColumn.type === 'radio',treeProps: isShowTreeStyle,is_sort_icon:onlyIconSort}":max-height="useVirtual?maxHeight||540:maxHeight"v-bind="$attrs"v-on="$listeners":highlight-current-row="highlightCurrentRow":border="table.border || isTableBorder":span-method="spanMethod || objectSpanMethod":cell-class-name="cellClassNameFuc"@sort-change="soltHandle"@row-click="rowClick"@cell-dblclick="cellDblclick"><!-- 主体内容 --><template v-for="(item, index) in renderColumns"><el-table-columnv-if="item.isShowCol === false ? item.isShowCol : true":key="index + 'i'":type="item.type":label="item.label":prop="item.prop":min-width="item['min-width'] || item.minWidth || item.width":sortable="item.sort || sortable":align="item.align || 'center'":fixed="item.fixed":show-overflow-tooltip="useVirtual?true:item.noShowTip?false:true"v-bind="{ ...item.bind, ...$attrs }"v-on="$listeners"><template slot-scope="scope">...</template></el-table-column></template></el-table></div>
</template><script>
export default {name: 'TTable',props: {// table所需数据table: {type: Object,default: () => {return {}}// required: true},// 表头数据columns: {type: Array,default: () => {return []}// required: true},...// Table最大高度maxHeight: {type: [String, Number]},// 是否开启虚拟列表useVirtual: {type: Boolean,default: false}},data() {return {tableData: this.table?.data,/*** 虚拟列表*/saveDATA: [], // 所有数据tableRef: null, // 设置了滚动的那个盒子tableWarp: null, // 被设置的transform元素fixLeft: null, // 固定左侧--设置的transform元素fixRight: null, // 固定右侧--设置的transform元素tableFixedLeft: null, // 左侧固定列所在的盒子tableFixedRight: null, // 右侧固定列所在的盒子scrollTop: 0,scrollNum: 0, // scrollTop / (itemHeight * pageList)start: 0,end: 30, // 3倍的pageListstarts: 0, // 备份ends: 30, // 备份pageList: 10, // 一屏显示itemHeight: 48 // 每一行高度}},watch: {'table.data': {handler(val) {if (this.useVirtual) {this.saveDATA = valthis.tableData = this.saveDATA.slice(this.start, this.end)} else {this.tableData = val}},deep: true // 深度监听},scrollNum(newV) {// 因为初始化时已经添加了3屏的数据,所以只有当滚动到第3屏时才计算位移量if (newV > 1) {this.start = (newV - 1) * this.pageListthis.end = (newV + 2) * this.pageListrequestAnimationFrame(() => {// 计算偏移量this.tableWarp.style.transform = `translateY(${this.start *this.itemHeight}px)`if (this.fixLeft) {this.fixLeft.style.transform = `translateY(${this.start *this.itemHeight}px)`}if (this.fixRight) {this.fixRight.style.transform = `translateY(${this.start *this.itemHeight}px)`}this.tableData = this.saveDATA.slice(this.start, this.end)})} else {requestAnimationFrame(() => {this.tableData = this.saveDATA.slice(this.starts, this.ends)this.tableWarp.style.transform = `translateY(0px)`if (this.fixLeft) {this.fixLeft.style.transform = `translateY(0px)`}if (this.fixRight) {this.fixRight.style.transform = `translateY(0px)`}})}}},created() {// 是否开启虚拟列表if (this.useVirtual) {this.init()}},mounted() {// 是否开启虚拟列表if (this.useVirtual) {this.initMounted()}},methods: {initMounted() {this.$nextTick(() => {// 设置了滚动的盒子this.tableRef = this.$refs['el-table'].bodyWrapper// 左侧固定列所在的盒子this.tableFixedLeft = document.querySelector('.el-table .el-table__fixed .el-table__fixed-body-wrapper')// 右侧固定列所在的盒子this.tableFixedRight = document.querySelector('.el-table .el-table__fixed-right .el-table__fixed-body-wrapper')/*** fixed-left | 主体 | fixed-right*/// 创建内容盒子divWarpPar并且高度设置为所有数据所需要的总高度let divWarpPar = document.createElement('div')// 如果这里还没获取到saveDATA数据就渲染会导致内容盒子高度为0,可以通过监听saveDATA的长度后再设置一次高度divWarpPar.style.height = this.saveDATA.length * this.itemHeight + 'px'// 新创建的盒子divWarpChildlet divWarpChild = document.createElement('div')divWarpChild.className = 'fix-warp'// 把tableRef的第一个子元素移动到新创建的盒子divWarpChild中divWarpChild.append(this.tableRef.children[0])// 把divWarpChild添加到divWarpPar中,最把divWarpPar添加到tableRef中divWarpPar.append(divWarpChild)this.tableRef.append(divWarpPar)// left改造let divLeftPar = document.createElement('div')divLeftPar.style.height = this.saveDATA.length * this.itemHeight + 'px'let divLeftChild = document.createElement('div')divLeftChild.className = 'fix-left'this.tableFixedLeft &&divLeftChild.append(this.tableFixedLeft.children[0])divLeftPar.append(divLeftChild)this.tableFixedLeft && this.tableFixedLeft.append(divLeftPar)// right改造let divRightPar = document.createElement('div')divRightPar.style.height = this.saveDATA.length * this.itemHeight + 'px'let divRightChild = document.createElement('div')divRightChild.className = 'fix-right'this.tableFixedRight &&divRightChild.append(this.tableFixedRight.children[0])divRightPar.append(divRightChild)this.tableFixedRight && this.tableFixedRight.append(divRightPar)// 被设置的transform元素this.tableWarp = document.querySelector('.el-table .el-table__body-wrapper .fix-warp')this.fixLeft = document.querySelector('.el-table .el-table__fixed .el-table__fixed-body-wrapper .fix-left')this.fixRight = document.querySelector('.el-table .el-table__fixed-right .el-table__fixed-body-wrapper .fix-right')this.tableRef.addEventListener('scroll', this.onScroll)})},// 初始化数据init() {this.saveDATA = this.table?.datathis.tableData = this.saveDATA.slice(this.start, this.end)},// 滚动事件onScroll() {this.scrollTop = this.tableRef.scrollTopthis.scrollNum = Math.floor(this.scrollTop / (this.itemHeight * this.pageList))}}
}
</script>

六、源码地址

GitHub源码地址

Gitee源码地址

基于ElementUi或Antd再次封装基础组件文档

vue3+ts基于Element-plus再次封装基础组件文档

相关文章:

vue+element-ui el-table组件二次封装实现虚拟滚动,解决数据量大渲染DOM过多而卡顿问题

一、此功能已集成到TTable组件中 二、最终效果 三、需求 某些页面不做分页时&#xff0c;当数据过多&#xff0c;会导致页面卡顿&#xff0c;甚至卡死 四、虚拟滚动 一、固定一个可视区域的大小并且其大小是不变的&#xff0c;那么要做到性能最大化就需要尽量少地渲染 DOM 元素…...

5.1 树和二叉树的定义

思维导图&#xff1a; 问题 为什么有树和二叉树&#xff1f; "树" 和 "二叉树" 都是数据结构中常用的结构&#xff0c;它们分别有其独特的应用和优点。我们可以从它们的定义和特性中理解为什么它们都存在。 1. **树 (Tree)&#xff1a;** - **定义**:…...

Java单元测试及常用语句 | 京东物流技术团队

1 前言 编写Java单元测试用例&#xff0c;即把一段复杂的代码拆解成一系列简单的单元测试用例&#xff0c;并且无需启动服务&#xff0c;在短时间内测试代码中的处理逻辑。写好Java单元测试用例&#xff0c;其实就是把“复杂问题简单化&#xff0c;建单问题深入化“。在编写的…...

详解Vue中的render: h => h(App)

声明:只是记录&#xff0c;会有错误&#xff0c;谨慎阅读 我们用脚手架初始化工程的时候&#xff0c;main.js的代码如下 import Vue from vue import App from ./App.vueVue.config.productionTip falsenew Vue({// 把app组件放入容器中render: h > h(App), }).$mount(#ap…...

归并排序的详解!

本文旨在讲解归并排序的实现&#xff08;递归及非递归&#xff09;搬好小板凳&#xff0c;干货来了&#xff01; 前序&#xff1a; 在介绍归并排序之前&#xff0c;需要给大家介绍的是什么是归并&#xff0c;归并操作&#xff0c;也叫归并算法&#xff0c;指的是将两个顺序序列…...

排盘程序算法探寻举例(陆先生八字)

算法实现&#xff1a; 1.庚生未月&#xff0c;燥土不能生金&#xff0c;日支申金为日主墙根&#xff0c;月干辛金比劫透出傍身&#xff0c;月干强。年干甲木自做寅木强根&#xff0c;又得月支乙木中气&#xff0c;甲木强旺有力&#xff0c;时干丙火七杀得未土余气&#xff0c;…...

考研408 | 【操作系统】终章

I/O设备的基本概念和分类 I/O设备&#xff1a; I/O设备的分类 1.按使用特性&#xff1a; 2.按传输速率分类&#xff1a; 3.按信息交换的单位分类&#xff1a; 总结&#xff1a; I/O控制器 I/O设备的机械部件&#xff1a; I/O设备的电子部件&#xff08;I/O控制器&#…...

亚马逊云科技生成式AI技术辅助教学领域,近实时智能应答2D数字人搭建

早在大语言模型如GPT-3.5等的兴起和被日渐广泛的采用之前&#xff0c;教育行业已经在AI辅助教学领域有过各种各样的尝试。在教育行业&#xff0c;人工智能技术的采用帮助教育行业更好地实现教学目标&#xff0c;提高教学质量、学习效率、学习体验、学习成果。例如&#xff0c;人…...

Programming abstractions in C阅读笔记:p139-p143

《Programming Abstractions In C》学习第55天&#xff0c;p139-p140&#xff0c;总结如下&#xff1a; 一、技术总结 1.文件I/O操作 文件I/O操作可以分为一下这些步骤&#xff1a; (1)声明文件指针对象。 File *infile;(2)打开文件 fopen()。打开文件的模式有“r”, “w…...

MyBatis-Plus学习笔记

1.MyBatis-Plus简介&#xff1a; MyBatis-Plus是一个MyBatis的增强工具&#xff0c;在MyBatis的基础上只做增强不做改变&#xff0c;为简化开发、提高效率而生。MyBatis-Plus提供了通用的mapper和service&#xff0c;可以在不编写任何SQL语句的情况下&#xff0c;快速的实现对单…...

linux安装docker全过程

3. 第二步&#xff1a;设置docker的存储库。就两条命令&#xff0c;我们直接执行就好。 ​ sudo yum install -y yum-utils sudo yum-config-manager --add-repo https://download.docker.com/linux/centos/docker-ce.repo ​​ 4. 安装docker engine和docker-compose。 执行命…...

Spring 中存取 Bean 的相关注解

目录 一、五大类注解 1、五大类注解存储Bean对象 1.1Controller(控制器储存) 1.2Service(服务存储) 1.3Repository(仓库存储) 1.4Component(组件存储) 1.5Configuration(配置存储) 2、五大类注解小结 2.1为什么要这么多类注解 2.2 五大类注解之间的关系 二、方法注解 1.方法注…...

Camunda 7.x 系列【38】表单服务 FormService

有道无术,术尚可求,有术无道,止于术。 本系列Spring Boot 版本 2.7.9 本系列Camunda 版本 7.19.0 源码地址:https://gitee.com/pearl-organization/camunda-study-demo 文章目录 1. 概述2. 演示2.1 获取流程开始表单2.2 启动流程2.3 查询任务表单2.4 完成任务3. 实际开发…...

保姆级教程之SABO-VMD-SVM的西储大学轴承诊断

之前写过一篇优化核极限学习机的轴承诊断&#xff0c;今天再出一期基于SVM的轴承诊断。 依旧是包含了从数据处理&#xff0c;到减法优化器SABO算法优化VMD参数&#xff0c;再到支持向量机的故障诊断&#xff0c;实现故障诊断的全流程&#xff0c;其他类型的故障诊断均可参考此流…...

指向任意节点的带环链表

&#x1f308;图示指向任意节点的带环链表 如图&#xff1a; &#x1f308;快慢指针法判断链表是否带环 &#x1f31f;思路&#xff1a;快指针fast一次走2步&#xff0c;慢指针slow一次走1步&#xff0c;fast先进环在换中运动&#xff0c;随后slow进入环。两指针每同时移动…...

应用于伺服电机控制、 编码器仿真、 电动助力转向、发电机、 汽车运动检测与控制的旋变数字转换器MS5905P

MS5905P 是一款 12bit 分辨率的旋变数字转换器。 片上集成正弦波激励电路&#xff0c;正弦和余弦允许输入峰峰值 幅度为 2.3V 到 4.0V &#xff0c;可编程激励频率为 10kHz 、 12kHz 、 15kHz 、 20kHz 。 转换器可并行或串行输出角度 和速度对应的数字量。 MS5905…...

Ansible学习笔记(持续更新)

Ansible学习目录 1.自动化运维1.1 企业实际应用场景1.1.1 Dev开发环境1.1.2 测试环境1.1.3 发布环境1.1.4 生产环境1.1.5 灰度环境 1.2 程序发布1.3 自动化运维应用场景1.4 常用自动化运维工具 2.Ansible介绍和架构2.1 Ansible特性2.2 Ansible架构2.2.1 Ansible主要组成部分2.2…...

CCF HPC China2023|澎峰科技:使能先进计算,赋能行业应用

CCF HPC China2023圆满落幕&#xff01; 桂秋八月&#xff0c;为期三天的中国高性能计算领域最高规格盛会——2023CCF全球高性能计算学术年会&#xff08;HPC China&#xff09;在青岛红岛国际展览中心圆满落幕。行业超算大咖、顶级学界精英、先锋企业领袖参会者齐聚山东青岛&a…...

【FlowDroid】一、处理流程学习

FlowDroid 一、处理流程学习 下载配置源码概况代码逻辑分析analyzeAPKFilerunInfoflowprocessEntryPointcalculateCallbacks(sourcesAndSinks)再次回到processEntryPoint 自己做一些笔记 下载配置 参照我前面的文章可以使用FlowDroid安装初体验 为了看代码了解FlowDroid如何处…...

MyBatis——MyBatis插件原理

摘要 本博文主要介绍MyBatis插件机原理&#xff0c;帮助大家更好的理解和学习MyBatis。 一、插件机制概述 MyBatis 允许你在已映射语句执行过程中的某一点进行拦截调用。默认情况下&#xff0c;MyBatis允许使用插件来拦截的方法调用包括&#xff1a; Executor (update, que…...

Qt/C++开发监控GB28181系统/取流协议/同时支持udp/tcp被动/tcp主动

一、前言说明 在2011版本的gb28181协议中&#xff0c;拉取视频流只要求udp方式&#xff0c;从2016开始要求新增支持tcp被动和tcp主动两种方式&#xff0c;udp理论上会丢包的&#xff0c;所以实际使用过程可能会出现画面花屏的情况&#xff0c;而tcp肯定不丢包&#xff0c;起码…...

质量体系的重要

质量体系是为确保产品、服务或过程质量满足规定要求&#xff0c;由相互关联的要素构成的有机整体。其核心内容可归纳为以下五个方面&#xff1a; &#x1f3db;️ 一、组织架构与职责 质量体系明确组织内各部门、岗位的职责与权限&#xff0c;形成层级清晰的管理网络&#xf…...

DBAPI如何优雅的获取单条数据

API如何优雅的获取单条数据 案例一 对于查询类API&#xff0c;查询的是单条数据&#xff0c;比如根据主键ID查询用户信息&#xff0c;sql如下&#xff1a; select id, name, age from user where id #{id}API默认返回的数据格式是多条的&#xff0c;如下&#xff1a; {&qu…...

Spring Cloud Gateway 中自定义验证码接口返回 404 的排查与解决

Spring Cloud Gateway 中自定义验证码接口返回 404 的排查与解决 问题背景 在一个基于 Spring Cloud Gateway WebFlux 构建的微服务项目中&#xff0c;新增了一个本地验证码接口 /code&#xff0c;使用函数式路由&#xff08;RouterFunction&#xff09;和 Hutool 的 Circle…...

PAN/FPN

import torch import torch.nn as nn import torch.nn.functional as F import mathclass LowResQueryHighResKVAttention(nn.Module):"""方案 1: 低分辨率特征 (Query) 查询高分辨率特征 (Key, Value).输出分辨率与低分辨率输入相同。"""def __…...

Razor编程中@Html的方法使用大全

文章目录 1. 基础HTML辅助方法1.1 Html.ActionLink()1.2 Html.RouteLink()1.3 Html.Display() / Html.DisplayFor()1.4 Html.Editor() / Html.EditorFor()1.5 Html.Label() / Html.LabelFor()1.6 Html.TextBox() / Html.TextBoxFor() 2. 表单相关辅助方法2.1 Html.BeginForm() …...

根目录0xa0属性对应的Ntfs!_SCB中的FileObject是什么时候被建立的----NTFS源代码分析--重要

根目录0xa0属性对应的Ntfs!_SCB中的FileObject是什么时候被建立的 第一部分&#xff1a; 0: kd> g Breakpoint 9 hit Ntfs!ReadIndexBuffer: f7173886 55 push ebp 0: kd> kc # 00 Ntfs!ReadIndexBuffer 01 Ntfs!FindFirstIndexEntry 02 Ntfs!NtfsUpda…...

Java求职者面试指南:Spring、Spring Boot、Spring MVC与MyBatis技术解析

Java求职者面试指南&#xff1a;Spring、Spring Boot、Spring MVC与MyBatis技术解析 一、第一轮基础概念问题 1. Spring框架的核心容器是什么&#xff1f;它的作用是什么&#xff1f; Spring框架的核心容器是IoC&#xff08;控制反转&#xff09;容器。它的主要作用是管理对…...

Kubernetes 节点自动伸缩(Cluster Autoscaler)原理与实践

在 Kubernetes 集群中&#xff0c;如何在保障应用高可用的同时有效地管理资源&#xff0c;一直是运维人员和开发者关注的重点。随着微服务架构的普及&#xff0c;集群内各个服务的负载波动日趋明显&#xff0c;传统的手动扩缩容方式已无法满足实时性和弹性需求。 Cluster Auto…...

yaml读取写入常见错误 (‘cannot represent an object‘, 117)

错误一&#xff1a;yaml.representer.RepresenterError: (‘cannot represent an object’, 117) 出现这个问题一直没找到原因&#xff0c;后面把yaml.safe_dump直接替换成yaml.dump&#xff0c;确实能保存&#xff0c;但出现乱码&#xff1a; 放弃yaml.dump&#xff0c;又切…...