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

前端性能优化 - 虚拟滚动

一 需求背景

需求:在一个表格里面一次性渲染全部数据,不采用分页形式,每行数据都有Echart图插入。
问题:图表渲染卡顿
技术栈:Vue、Element UI
卡顿原因:页面渲染时大量的元素参与到了重排的动作中,性能差
优化方案:虚拟滚动

二 虚拟滚动原理

虚拟滚动其实就是综合数据分页和无限滚动的方法,在有限的视口中只渲染我们所能看到的数据,超出视口之外的数据就不进行渲染,可以通过计算可视范围内的单元格,保证每一次滚动渲染的DOM元素都是可以控制的,不会担心像数据分页一样一次性渲染过多,也不会发生像无限滚动方案那样会存在数据堆积,是一种很好的解决办法。

假设实际开发中服务端一次响应20万条列表数据,此时设备屏幕只允许容纳20条,那么用户理论上只可以看见20条数据,其他的数据不会进行渲染加载。如果前端将20万条数据全部渲染成DOM元素,可能造成程序卡顿,占用较大资源,非常影响用户体验,那么虚拟滚动技术就完美的解决了这一问题。

在这里插入图片描述

可以计算:卷入行数 = scrollTop(卷入高度) / 每行的高度(itemH)

如何计算可视区域渲染的元素以及实现虚拟滚动,步骤如下:

  • 统一设置每一行的高度需要相同,方便计算。
  • 需要计算渲染数据数量(数组的长度),根据每行的高度以及元素的总量计算整个DOM渲染容器的高度。
  • 获取可视区域的高度
  • 触发滚动事件后,计算偏移量(滚动条据顶距离),再根据可视区域高度计算本次偏移的截止量,得到需要渲染的具体数据。
  • 对于与表格的列来说,需要做虚拟滚动的话,在x轴同样可以根据以上步骤执行,实现横向虚拟滚动。

三 项目具体代码:

  <el-tableref="latestPositionRef"v-loading="tableLoading"class="table-fixed"size="mini":data="sliceTable"height="355px":cell-style="cellStyle"row-key="secId"@sort-change="handleSortChange"@selection-change="handleSelectionChange"><el-table-column type="selection" width="55" align="center" :reserve-selection="true" /><el-table-column label="排名" width="50px" align="center" fixed><template slot-scope="scope">{{ scope.$index + 1 }}</template></el-table-column><DynamicColumnv-for="(ite, index) in overviewColumns":key="index":item="ite":empty="1":data-list="infoList"table-sign="latest-position-list":schemas="overviewColumns"@changeColumn="(cols)=>{overviewColumns=cols;}"/><el-table-column label="备注设置" align="center" width="50" fixed="right"><template slot-scope="scope"><el-buttonv-if="scope.row.secId"type="text"size="mini"plain@click="setRemark(scope.row)">查看</el-button></template></el-table-column></el-table>
data() {return {// 动态列-ETF精选overviewColumns: this.$columns.getColumns('etf_selected_list'),// 表格数据infoList: [],showInfoList: [],// 开始索引startIndex: 0,// 空元素,用于撑开table的高度vEle: undefined,// 每一行高度itemHeight: 42,}
}
watch: {sliceTable: {handler() {// 解决表格错位问题this.$nextTick(() => {this.$refs.latestPositionRef.doLayout()})},deep: true},
}
  async created() {// 创建一个空元素,这个空元素用来撑开 table 的高度,模拟所有数据的高度this.vEle = document.createElement('div')},
  computed: {// 这个是截取表格中的部分数据,放到了 table 组件中来显示sliceTable() {return this.showInfoList.slice(this.startIndex, this.startIndex + 6)},}
 /** 加载ETF精选列表数据 */loadData() {console.log('loadData')const start_i = this.showInfoList.lengthfor (let i = start_i; i < start_i + 10; i++) {this.showInfoList.push(this.infoList[i])}this.$nextTick(() => {// 设置成绝对定位,这个元素需要我们去控制滚动this.$refs.latestPositionRef.$el.querySelector('.el-table__body').style.position = 'absolute'// 计算表格所有数据所占内容的高度this.vEle.style.height =this.showInfoList.length * this.itemHeight + 'px'// 把这个节点加到表格中去,用它来撑开表格的高度this.$refs.latestPositionRef.$el.querySelector('.el-table__body-wrapper').appendChild(this.vEle)// 重新设置曾经被选中的数据this.selection.forEach((row) => {this.$refs.latestPositionRef.toggleRowSelection(row, true)})})},tableScroll() {console.log('tableScroll')const bodyWrapperEle = this.$refs.latestPositionRef.$el.querySelector('.el-table__body-wrapper')console.log(bodyWrapperEle, 'bodyWrapperEle')// 滚动的高度const scrollTop = bodyWrapperEle.scrollTop// 下一次开始的索引this.startIndex = Math.floor(scrollTop / this.itemHeight)// 滚动操作bodyWrapperEle.querySelector('.el-table__body').style.transform = `translateY(${this.startIndex * this.itemHeight}px)`// 滚动操作后,上面的一些 tr 没有了,所以需要重新设置曾经被选中的数据this.selection.forEach((row) => {this.$refs.latestPositionRef.toggleRowSelection(row, true)})// 滚动到底,加载新数据if (bodyWrapperEle.scrollHeight <=scrollTop + bodyWrapperEle.clientHeight) {if (this.showInfoList.length === this.infoList.length) {this.$message.warning('没有更多了')return}this.loadData()// 解决el-table中内容错位// this.$nextTick(() => {//   this.$refs.latestPositionRef.doLayout()// })}}

四 其他方案:

分段渲染、数据分页、无限滚动

学习:https://www.modb.pro/db/122781

http://www.lryc.cn/news/203981.html

相关文章:

  • 手写 Promise(1)核心功能的实现
  • 深入探究Java内存模型
  • 深度学习 | Pytorch深度学习实践 (Chapter 10、11 CNN)
  • 谈谈你对spring boot 3.0的理解
  • 【大数据】Hadoop
  • Spring实例化源码解析之Bean的实例化(十二)
  • git常用的几条命令介绍
  • 使用VisualSVN在Windows系统上设置SVN服务器,并结合内网穿透实现公网访问
  • 第18章 SpringCloud生态(三)
  • leetcode:2347. 最好的扑克手牌(python3解法)
  • 2007-2022 年上市公司国内外专利授权情况数据
  • 安全渗透测试网络基础知识之路由技术
  • 【大数据】Kafka 实战教程(二)
  • React 框架
  • 数据结构与算法之图: Leetcode 133. 克隆图 (Typescript版)
  • illuminate/database 使用 一
  • 前端koa搭建服务器(保姆级教程)——part1
  • js逆向第一课 密码学介绍
  • Dynamic DataSource 多数据源配置【 Springboot + DataSource + MyBatis Plus + Druid】
  • MyBatis:配置文件
  • ARM,基础、寄存器
  • FC-TSGAS-1624 CP451-10 MVI56E-MNETC IC697CMM742
  • 异或运算.
  • NewStarCTF2023week4-逃(反序列化字符串逃逸)
  • PyTorch Tensor 形状
  • RabbitMQ运行机制和通讯过程介绍
  • UE4 TextRender显示中文方法
  • C++动态规划算法的应用:得到 K 个半回文串的最少修改次数 原理源码测试用例
  • Pyside6 QFileDialog
  • Leetcode1793. Maximum Score of a Good Subarray