性能优化 | el-table中内嵌大量el-input控件导致渲染卡顿的问题

server/2024/12/22 13:08:40/

场景

项目中有一个应用场景,用户需要在表单中大量使用选择框以及输入框填写数据(每一行大概有三十几个输入框),当选择框与输入框达到一定数量的时候,页面会出现输入不连续、卡顿的现象,如下图:
需求原型.png

排查

查了一下网上的解决方案,大体是因为页面渲染过多的el-input输入框给浏览器带来较大的渲染压力导致的。基于这点,对项目作出优化。

引入虚拟列表

虚拟列表,简单来说就是页面并不渲染表格中的所有数据,只渲染可视窗口部分的数据,当用户拖动滚动条滚动的时候,逐渐加载数据。这个适用于表格中需要渲染大量数据的场景,从而减轻浏览器的渲染压力。

对于elementui中使用el-table加入虚拟列表,可以参考下面这篇文章:
element全网最简单el-table实现虚拟列表

参考上面的文章,做了一个初版的效果。但由于虚拟列表只渲染部分数据的特性,所以一定会带来一个缺陷,那就是会短暂出现白屏的问题,即用户看到未渲染的区域。排查原因得知是因为el-table的占位区域append设置为定高totalHeight

<!-- 占位 -->
<template #append><div :style="{ height: `${totalHeight}px` }"></div>
</template>...
totalHeight() {return this.tableData.length * this.itemHeight
}

经过思考,改成动态变化的高度,从而形成一种滚动触底更新的效果:

// 改变append的高度
updated() {if (this.$refs.multipleTable) {const bodyEl = this.$refs.multipleTable.$el.getElementsByTagName("tbody")[0]if (bodyEl.offsetHeight) {this.totalHeight = bodyEl.offsetHeight} else if (bodyEl.offsetHeight === 0 && this.tableData.length === 0) {this.totalHeight = 0}// 重新绘制表格this.$refs.multipleTable.doLayout()}
},

虚拟列表再优化

引入虚拟列表后,加载确实快不少,输入卡顿的显现也明显优化。但发现一个问题,原有的选择方法不生效,当用户滚动的时候,原有的选择项被清空。那么,如何记录选择列表的状态呢?elementui提供了一个reserve-selection的API,用于记录列表项的选择状态:
reserve-selection.png

当然,也需要重写@select="handleSelect"@select-all="handleSelectAll"方法:

// 监听选择全部
handleSelectAll() {if (this.isSelectAll) {this.tableData.forEach(item => {this.$refs.multipleTable.toggleRowSelection(item, false)})this.selectList = []this.isSelectAll = false} else {this.tableData.forEach(item => {this.$refs.multipleTable.toggleRowSelection(item, true)})this.selectList = this.tableDatathis.isSelectAll = true}
},
// 手动触发选择事件
handleSelect(selection) {this.selectList = selectionif (selection.length === this.tableData.length) {this.isSelectAll = true} else {this.isSelectAll = false}
},

引入虚拟输入框

引入虚拟列表后,效果确实提升了不少,至少在大数据量的情况下,不再会出现卡顿了(因为大数据量其实只渲染固定的数据)。但还有可以优化的空间吗?
我们之前提到,造成页面卡顿的原因,就是大量渲染了el-input以及el-select控件。那么,可以再进一步优化,以减少控件的渲染数量吗?我在这篇文章中找到了灵感:
vue大数据表格卡顿问题的完美解决方案
或者直接看文章提到的demo:
Demo
DEMO.png
核心思路就是,**只有当需要编辑的时候,才显示输入框,其余都显示内容。**这么一来,最多只会渲染1个编辑控件,卡顿问题可以进一步得到优化。

实现思路

  • template中根据条件选择渲染编辑控件,默认显示表格内容(同时通过修改样式模拟输入框必填标红);
  • 鼠标点击表格的时候,记录当前表格的坐标,用于切换编辑控件;
  • 页面根据渲染条件,切换编辑控件,同时自动聚焦到编辑框中。

示例代码如下所示:

<el-form-item :prop="'displayList.' + $index + '.出差地点'"><el-input v-if="focusCell === `${$index}-出差地点`" type="textarea" v-model="row['出差地点']" @blur="beforeComputedAll" autosize></el-input><div v-else :class="row['出差地点'] ? 'm-input' : 'custom-required'">{{ row['出差地点'] }}</div>
</el-form-item>

记录表格坐标可以使用el-table的@cell-click方法:

// 聚焦单元格
updateFocusCell(row, col, cell) {if (this.focusDebounce) {clearTimeout(this.focusDebounce)this.focusDebounce = null}this.focusDebounce = setTimeout(() => {const lineNo = row['行号']const prop = col.propertyconst idx = this.displayList.findIndex(item => item['行号'] === lineNo)if (this.focusCell !== `${idx}-${prop}`) {this.focusCell = `${idx}-${prop}`this.$nextTick(() => {target.focus()})}this.focusDebounce = null}, 20)
},

最终效果

最终效果.jpg

总结

至此,表格已经从一开始的输入卡顿,到现在基本是可以流畅输入了,使用体验得到大大的提升。当然还有优化的空间,比如校验逻辑的编写,以及一些相关的计算等等,那块就是属于算法层面的东西了,在此就不展开叙述。
对于表格大批量数据渲染的优化方案,总结如下:

  • 引入虚拟列表,只渲染可视化区域的数据;
  • 模拟编辑控件,仅在需要编辑的时候才展示;
  • 计算逻辑优化,减少计算时间。

第一次遇到这种类型的优化,觉得挺有意思的,在此做一个记录。如果大家有更好的优化方案也可以在评论区提出,欢迎大家一起学习~


http://www.ppmy.cn/server/40548.html

相关文章

C++Primer Plus第五章结构编程练习

编程练习 1.编写一个要求用户输入两个整数的程序。该程序将计算并输出这两个整数之间(包括这两个整数)所有整数的和。这里假设先输入较小的整数。例如&#xff0c;如果用户输入的是2和9&#xff0c;则程序将指出2~9之间所有整数的和为 44。 2.使用 array对象(而不是数组)和lo…

视频拼接融合产品的产品与架构设计(二)

视频拼接融合产品的产品与架构设计一 以上是第一期&#xff0c;以前思考的时候还是比较着急&#xff0c;现在思考的更多了&#xff0c;现实世界的拼接更加需要我们沉下心来做&#xff0c;尤其是对于更多画面&#xff0c;画面更加清晰怎么做 本篇章不在于其他功能&#xff0c;在…

西安天童美语|世界家庭医生日:做守护孩子健康的坚实后盾

每年的5月19日&#xff0c;我们迎来了世界家庭医生日。这一节日旨在强调家庭医生在维护家庭成员健康中的重要地位&#xff0c;尤其对于幼儿的健康成长具有深远影响。      家庭医生&#xff0c;作为孩子健康的第一道防线&#xff0c;他们在孩子的健康管理中扮演着至关重要的…

ASP.NET学生信息管理系统

摘 要 本文介绍了在ASP.net环境下采用“自上而下地总体规划&#xff0c;自下而上地应用开发”的策略开发一个管理信息系统的过程。通过分析某一学校学生管理的不足&#xff0c;创建了一套行之有效的计算机管理学生的方案。文章介绍了学生管理信息系统的系统分析部分&#xff0c…

【SpringBoot】解锁后端测试新境界:学习Mockito与MockMvc的单元测试魔法

文章目录 前言&#xff1a;Java常见的单元测试框架一.Junit5基础二.SpringBoot项目单元测试1.添加依赖2.SpringBoot单元测试标准结构3.SpringBoot单元测试常用注解 三.单元测试中如何注入依赖对象1.真实注入&#xff08;AutoWired、 Resource&#xff09;2.Mock注入2.1.前言2.2…

栈和队列OJ练习题及解答

前言 上一篇博客已经讲到了栈和队列的数据结构&#xff0c;概括一下&#xff1a;栈后进先出&#xff08;Last In First Out&#xff09;、队列先进先出&#xff08;First In First Out&#xff09;。那么&#xff0c;接下来就来讲讲&#xff0c;关于栈和队列的相关练习题&#…

【Python快速上手(二十一)】

目录 Python快速上手&#xff08;二十一&#xff09;Python3 使用数据库-mysql-connector1. 创建数据库连接2. 创建数据表3. 插入数据4. 查询数据5. 使用 WHERE 条件语句6. 排序7. 删除记录8. 更新表数据9. 删除表10.异常处理总结 Python快速上手&#xff08;二十一&#xff09…

Redis7降级6备份不过期数据操作

Redis7降级6备份不过期数据操作 搜到三种备份方法 rdb版本11-》redis7;rdb版本9-》redis6&#xff1b;不兼容&#xff0c;版本太高无第三方工具转换。其中那个rdbtool白瞎断更好久了。aof 使用aof -fix&#xff0c;文件大小没变&#xff0c;读取不了数据&#xff1b;不兼容&a…