news 2026/6/10 20:07:06

当表格数据量过大的时候,如何使用不分页进行展示

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
当表格数据量过大的时候,如何使用不分页进行展示

表格数据过大的时候,渲染时间则增大。使用分页就可以解决,但是有的时候用户不想使用分页,那么前端可以监听滚动条进行懒加载,但是当数据都加载出来。数据量就又大了。

1. 组件文件名: elTableVirtualScroll.vue 通过仅渲染可视区域内的内容来减少 DOM 节点数量,从而提升页面响应速度和内存使用效率

<template> <div> <slot></slot> </div> </template> <script> import throttle from 'lodash/throttle' export default { name: 'el-table-virtual-scroll', props: { data: { type: Array, required: true }, height: { type: Number, default: 60 }, buffer: { type: Number, default: 500 }, keyProp: { type: String, default: 'id' }, throttleTime: { type: Number, default: 100 } }, data () { return { sizes: {} // 尺寸映射(依赖响应式) } }, computed: { // 计算出每个item(的key值)到滚动容器顶部的距离 offsetMap ({ keyProp, height, sizes, data }) { const res = {} let total = 0 for (let i = 0; i < data.length; i++) { const key = data[i][keyProp] res[key] = total const curSize = sizes[key] const size = typeof curSize === 'number' ? curSize : height total += size } return res } }, methods: { // 初始化数据 initData () { // 可视范围内显示数据 this.renderData = [] // 页面可视范围顶端、底部 this.top = undefined this.bottom = undefined // 截取页面可视范围内显示数据的开始和结尾索引 this.start = 0 this.end = undefined this.scroller = this.$el.querySelector('.el-table__body-wrapper') // 初次执行 setTimeout(() => { this.handleScroll() }, 100) // 监听事件 this.onScroll = throttle(this.handleScroll, this.throttleTime) this.scroller.addEventListener('scroll', this.handleScroll) window.addEventListener('resize', this.onScroll) }, // 更新尺寸(高度) updateSizes () { const rows = this.$el.querySelectorAll('.el-table__body > tbody > .el-table__row') Array.from(rows).forEach((row, index) => { const item = this.renderData[index] if (!item) return const key = item[this.keyProp] const offsetHeight = row.offsetHeight if (this.sizes[key] !== offsetHeight) { this.$set(this.sizes, key, offsetHeight) } }) }, // 处理滚动事件 handleScroll (shouldUpdate = true) { // 更新当前尺寸(高度) this.updateSizes() // 计算renderData this.calcRenderData() // 计算位置 this.calcPosition() shouldUpdate && this.updatePosition() // 触发事件 this.$emit('change', this.renderData, this.start, this.end) }, // 获取某条数据offsetTop getOffsetTop (index) { const item = this.data[index] if (item) { return this.offsetMap[item[this.keyProp]] || 0 } return 0 }, // 获取某条数据的尺寸 getSize (index) { const item = this.data[index] if (item) { const key = item[this.keyProp] return this.sizes[key] || this.height } return this.height }, // 计算只在视图上渲染的数据 calcRenderData () { const { scroller, data, buffer } = this // 计算可视范围顶部、底部 const top = scroller.scrollTop - buffer const bottom = scroller.scrollTop + scroller.offsetHeight + buffer // 二分法计算可视范围内的开始的第一个内容 let l = 0 let r = data.length - 1 let mid = 0 while (l <= r) { mid = Math.floor((l + r) / 2) const midVal = this.getOffsetTop(mid) if (midVal < top) { const midNextVal = this.getOffsetTop(mid + 1) if (midNextVal > top) break l = mid + 1 } else { r = mid - 1 } } // 计算渲染内容的开始、结束索引 let start = mid let end = data.length - 1 for (let i = start + 1; i < data.length; i++) { const offsetTop = this.getOffsetTop(i) if (offsetTop >= bottom) { end = i break } } // 开始索引始终保持偶数,如果为奇数,则加1使其保持偶数【确保表格行的偶数数一致,不会导致斑马纹乱序显示】 if (start % 2) { start = start - 1 } // console.log(start, end, 'start end') this.top = top this.bottom = bottom this.start = start this.end = end this.renderData = data.slice(start, end + 1) }, // 计算位置 calcPosition () { const last = this.data.length - 1 // 计算内容总高度 const wrapHeight = this.getOffsetTop(last) + this.getSize(last) // 计算当前滚动位置需要撑起的高度 const offsetTop = this.getOffsetTop(this.start) // 设置dom位置 const classNames = ['.el-table__body-wrapper', '.el-table__fixed-right .el-table__fixed-body-wrapper', '.el-table__fixed .el-table__fixed-body-wrapper'] classNames.forEach(className => { const el = this.$el.querySelector(className) if (!el) return // 创建wrapEl、innerEl if (!el.wrapEl) { const wrapEl = document.createElement('div') const innerEl = document.createElement('div') wrapEl.appendChild(innerEl) innerEl.appendChild(el.children[0]) el.insertBefore(wrapEl, el.firstChild) el.wrapEl = wrapEl el.innerEl = innerEl } if (el.wrapEl) { // 设置高度 el.wrapEl.style.height = wrapHeight + 'px' // 设置transform撑起高度 el.innerEl.style.transform = `translateY(${offsetTop}px)` // 设置paddingTop撑起高度 // el.innerEl.style.paddingTop = `${offsetTop}px` } }) }, // 空闲时更新位置 updatePosition () { this.timer && clearTimeout(this.timer) this.timer = setTimeout(() => { this.timer && clearTimeout(this.timer) // 传入false,避免一直循环调用 this.handleScroll(false) }, this.throttleTime + 10) }, // 【外部调用】更新 update () { console.log('update') this.handleScroll() }, // 【外部调用】滚动到第几行 scrollTo (index, stop = false) { const item = this.data[index] if (item && this.scroller) { this.updateSizes() this.calcRenderData() this.$nextTick(() => { const offsetTop = this.getOffsetTop(index) this.scroller.scrollTop = offsetTop // 调用两次scrollTo,第一次滚动时,如果表格行初次渲染高度发生变化时,会导致滚动位置有偏差,此时需要第二次执行滚动,确保滚动位置无误 if (!stop) { setTimeout(() => { this.scrollTo(index, true) }, 50) } }) } }, // 【外部调用】重置 reset () { this.sizes = {} this.scrollTo(0, false) } }, watch: { data () { this.update() } }, created () { this.$nextTick(() => { this.initData() }) }, beforeDestroy () { if (this.scroller) { this.scroller.removeEventListener('scroll', this.onScroll) window.removeEventListener('resize', this.onScroll) } } } </script> <style lang='less' scoped> </style>

2. 使用案例:在文件中引入组件,tableData1变量是表格全部数据,tableData则是展示的数据,tableDataChange用来监听容器滚动条,进而计算展示数据

<VirtualScroll :data="tableData1" :height="62" key-prop="id" @change="tableDataChange"> <el-table :data="tableData" border ref="tables" height="50vh" v-loading="tableLoading" style="width: 100%;"> </el-table> </VirtualScroll>
tableDataChange(renderData) { this.tableData = renderData if(this.tableData.length > 0) { this.tableData.unshift(this.formObject) } },
版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/6/10 13:39:59

“AIE未来视听领袖峰会”在澳门举办,聚焦AI+AR新范式

12月4日&#xff0c;作为首届全球智能机械与电子产品博览会&#xff08;AIE&#xff09;的同期活动&#xff0c;“AIE未来视听领袖峰会”在澳门威尼斯人金光会展中心举行。会议以“视听全球&#xff0c;音画未来”为主题&#xff0c;汇聚300余位来自全球的行业领袖、院士专家、…

作者头像 李华
网站建设 2026/6/10 6:09:35

Linux 基础 IO 核心知识点梳理

Linux 基础 IO&#xff08;输入 / 输出&#xff09;是操作系统与外部设备、文件进行数据交互的核心机制&#xff0c;其底层围绕文件描述符展开&#xff0c;遵循 “一切皆文件” 的设计理念。以下是基础 IO 的核心概念与操作梳理&#xff1a;一、 核心概念一切皆文件Linux 中&am…

作者头像 李华
网站建设 2026/6/10 21:25:20

快速部署EmotiVoice:一键生成带情感的AI语音

快速部署EmotiVoice&#xff1a;一键生成带情感的AI语音 在智能语音助手越来越“懂人心”的今天&#xff0c;我们早已不满足于那种机械朗读式的TTS&#xff08;文本转语音&#xff09;。想象一下&#xff0c;当你的虚拟客服用带着关切语气说“您别担心”&#xff0c;或是游戏角…

作者头像 李华
网站建设 2026/6/10 14:45:33

EmotiVoice语音合成系统安装依赖项清单及配置建议

EmotiVoice语音合成系统安装依赖项清单及配置建议 在智能语音交互日益普及的今天&#xff0c;用户早已不再满足于“能说话”的机器&#xff0c;而是期待听到更自然、有情感、像真人一样的声音。从虚拟主播到游戏NPC&#xff0c;从有声读物到客服机器人&#xff0c;传统文本转语…

作者头像 李华
网站建设 2026/6/10 12:43:10

AI元人文构想:价值星图的部署与迭代——更新中的新华字典

AI元人文构想&#xff1a;价值星图的部署与迭代——更新中的新华字典&#xff08;综合修订版&#xff09;摘要本文提出一种名为“价值星图”的AI元人文基础设施构想。该构想将人类多元价值体系编码为可计算、可查询的标准化图谱&#xff0c;并通过分布式主权云节点进行部署&…

作者头像 李华
网站建设 2026/6/10 12:32:13

【新手入手arduino 数组流水灯】

新手入手arduino 数组流水灯 结果展示&#xff1a; 新手入手arduino 数组流水灯你将需要以下组件&#xff1a; 1 Arduino UNO 板 5 330欧姆电阻 5 LED 代码 const byte LEDs[]{6,7,8,9,10}; const byte total 5; byte index 0; void setup() { // put your setu…

作者头像 李华