干货|antd组件库Table组件开启虚拟列表的影响
注:虚拟滚动或分页加载可能导致部分数据未被渲染在 DOM 中,导致复制操作只选中页面上已渲染的数据。
导致问题:在开启虚拟列表时,复制表格内容到excel表时表头能正常显示,但所有数据都整齐排列在第一列的情况
原因分析
-
虚拟列表的渲染机制:虚拟列表只会渲染部分 DOM 元素(即当前可视区域的数据),当你复制时,浏览器可能认为整个表格内容都在第一列中,因为 DOM 中并没有完整渲染每一列的数据,导致数据在复制时被错误地认为是一列。
-
浏览器复制机制:浏览器复制操作依赖于 DOM 结构。如果数据列在虚拟列表中未被渲染到 DOM 中,浏览器在处理复制操作时会错位解析,将所有的未渲染数据都视为属于第一列。
-
列内容渲染方式:虚拟表格可能在列的
render
函数中对内容进行了延迟渲染或部分优化。由于这些列的内容未被完全渲染,浏览器可能只捕获到了render
的部分结果,而这些结果被视为属于同一列。
解决方案
如果你要在启用虚拟列表的同时保持复制功能的正常工作,可以尝试以下方法:
-
自定义复制功能:实现一个自定义的复制功能,手动从
dataSource
中提取表格数据,并将这些数据格式化为适合 Excel 的形式。这种方式不会依赖 DOM 中的实际内容,而是直接从表格的数据源获取数据。 -
在复制前暂时关闭虚拟化:在用户执行复制操作时,可以监听
onCopy
事件并暂时关闭虚拟列表的功能,确保所有数据都完整渲染到 DOM 中,复制完成后再重新启用虚拟化。 -
使用
copy-to-clipboard
库:使用copy-to-clipboard
等库自定义复制行为,从表格数据源中获取所选内容,并以合适的格式进行复制,而不是依赖浏览器的默认复制行为。
1:自定义复制功能
可以在表格的顶部加一个按钮,允许用户点击后将表格内容复制到剪贴板。
import copy from 'copy-to-clipboard';
const handleCopy = () => {
const dataToCopy = historyData.map(row =>
columns.map(col => row[col.dataIndex]).join('\t')
).join('\n');
copy(dataToCopy);
message.success('表格数据已复制到剪贴板');
};
// 在表格上方添加按钮
<Button onClick={handleCopy}>复制表格数据</Button>
注:但是这种方式需要一个按钮
推荐结合后端实现滚动底部动态加载数据 :
1、滚动加载函数:
// 监听滚动事件,当用户滚动到表格底部时加载更多数据
const handleScroll = (e) => {
const { scrollTop, scrollHeight, clientHeight } = e.target;
if (scrollTop + clientHeight >= scrollHeight - 50 && hasMore) {
setPageNum((prevPage) => prevPage + 1);
// console.log('底部xxxx=>>', scrollTop);
// console.log('target=>>', e.target);
}
};
2、Table组件
<Table
size='middle'
bordered
columns={columns}
dataSource={historyData}
rowKey='index'
pagination={false}
scroll={{ y: 300 }}
loading={loading}
// TODO:逻辑滚动时加载数据
onScroll={handleScroll}
// virtual //关闭虚拟列表
/>
设置了滚动到底部加载数据的机制,虚拟列表的作用就相对减弱了。虚拟列表主要是为了在数据量特别大的情况下优化渲染性能,但既然通过滚动加载数据来控制数据量,虚拟列表不再必要。可以考虑去掉虚拟列表,尤其是在复制功能上,如果去掉后问题解决了,那就说明虚拟列表确实是导致问题的原因。