在近日的开发中遇到了一次渲染大量数据,导致渲染缓慢的问题,数据量大是第一点,第二点是使用了antd的Typography中的Text去进行展示Text组件包含ellipsis去控制文本过长时展示tooltip。
Ant Design 的 Text 组件的 ellipsis 属性不是简单使用 CSS 的 text-overflow: ellipsis,而是采用了复杂的 JavaScript 实现。
-
二分查找算法:使用二分搜索来确定截断位置,需要不断测量文本高度
-
动态测量:需要创建隐藏的测量元素来计算文本尺寸
-
混合测量法:结合 CSS 和 JS 来处理复杂的布局情况
另外Text组件的 ellipsis 实现,需要动态创建测量用的 span 元素,反复计算元素的 clientHeight,多次 DOM 重排和重绘以及处理 tooltip 的显示逻辑等等所以大量的Text组件渲染会导致渲染速度慢,
解决方案:1可以尝试用普通css代替完成效果,可以减去很多额外的计算。不需要测量和重新计算,可以用GPU帮忙计算等等
2,使用虚拟化滚动创建虚拟列表
npm install rc-virtual-list
import VirtualList from "rc-virtual-list";
// 在 CloudStorage 组件中添加以下代码:
const CloudStorage = () => {
// 现有的 state...
// 新增:容器高度和每项高度配置
const folderContainerRef = useRef<HTMLDivElement>(null);
const FOLDER_ITEM_HEIGHT = 120; // FolderCard 高度 + margin
const CONTAINER_HEIGHT = 600; // 或动态计算
// 计算容器高度(可选,根据实际需求调整)
const containerHeight = folderContainerRef.current
? folderContainerRef.current.clientHeight
: CONTAINER_HEIGHT;
// 渲染虚拟化的文件夹列表
const renderVirtualFolderList = () => {
if (folderList.length === 0) return null;
return (
<div
className="folder-list-container"
ref={folderContainerRef}
style={{ height: containerHeight }}
>
<VirtualList
data={folderList}
height={containerHeight}
itemHeight={FOLDER_ITEM_HEIGHT}
itemKey="uuid"
>
{(item, index) => (
<div
key={item.uuid}
style={{
padding: "10px",
display: "inline-block",
width: "calc(25% - 20px)", // 4列布局,可根据需要调整
marginRight: "20px",
marginBottom: "20px",
}}
>
<FolderCard
data={{
id: item.uuid,
icn:
item?.directory?.directory_category === 3 ||
item?.directory?.directory_category === 7
? require("@/assets/img/icn_file_share.png")
: require("@/assets/img/icn_file.png"),
title: item.name,
content: item?.directory?.directory_desc,
}}
disabledHandler={
item.directory?.directory_category === 6 ||
item.directory?.directory_category === 7
}
onDoubleClick={() => {
const current = {
uuid: item.uuid,
name: item.name,
directory_category: item?.directory?.directory_category,
directory_level: item?.directory?.directory_level,
permission: item.permission,
};
setBreadcrumb([...breadcrumb, current]);
setCurrentPathData(current);
}}
handlerOptions={calcHandlerOptions(item)}
onClickHandler={(key: any) => onClickHandler(key, item)}
/>
</div>
)}
</VirtualList>
</div>
);
};
// 在原来的渲染位置替换:
return (
<div>
{/* 其他代码... */}
{folderList.length > 0 && (
<>
<Flex justify="space-between">
<div className="font-bold">文件夹</div>
<FilterSortSelect
options={[
{ value: "created_at", label: "按创建时间" },
{ value: "target_size", label: "按文件大小" },
]}
value={folerFilterValue}
onChange={(value: any) => setFolderFilterValue(value)}
/>
</Flex>
{/* 替换原来的 Space wrap */}
{renderVirtualFolderList()}
</>
)}
{/* 其他代码... */}
</div>
);
};
或者使用
import VirtualList from "rc-virtual-list";
const CloudStorage = () => {
// 网格配置
const COLUMNS_PER_ROW = 4; // 每行显示的列数
const FOLDER_CARD_WIDTH = 200; // 每个卡片的宽度
const FOLDER_CARD_HEIGHT = 100; // 每个卡片的高度
const ROW_HEIGHT = FOLDER_CARD_HEIGHT + 20; // 行高(包含间距)
// 将一维数组转换为二维数组(按行分组)
const groupedFolderList = useMemo(() => {
const groups = [];
for (let i = 0; i < folderList.length; i += COLUMNS_PER_ROW) {
groups.push(folderList.slice(i, i + COLUMNS_PER_ROW));
}
return groups;
}, [folderList]);
const renderVirtualGridFolderList = () => {
if (groupedFolderList.length === 0) return null;
return (
<div
className="folder-grid-container"
style={{ height: 600, width: "100%" }}
>
<VirtualList
data={groupedFolderList}
height={600}
itemHeight={ROW_HEIGHT}
itemKey={(item, index) => `row-${index}`}
>
{(rowItems, rowIndex) => (
<div
key={`row-${rowIndex}`}
style={{
display: "flex",
gap: "20px",
padding: "10px 0",
height: ROW_HEIGHT,
}}
>
{rowItems.map((item: any) => (
<FolderCard
key={item.uuid}
data={{
id: item.uuid,
icn:
item?.directory?.directory_category === 3 ||
item?.directory?.directory_category === 7
? require("@/assets/img/icn_file_share.png")
: require("@/assets/img/icn_file.png"),
title: item.name,
content: item?.directory?.directory_desc,
}}
disabledHandler={
item.directory?.directory_category === 6 ||
item.directory?.directory_category === 7
}
onDoubleClick={() => {
const current = {
uuid: item.uuid,
name: item.name,
directory_category: item?.directory?.directory_category,
directory_level: item?.directory?.directory_level,
permission: item.permission,
};
setBreadcrumb([...breadcrumb, current]);
setCurrentPathData(current);
}}
handlerOptions={calcHandlerOptions(item)}
onClickHandler={(key: any) => onClickHandler(key, item)}
/>
))}
</div>
)}
</VirtualList>
</div>
);
};
// 使用方式相同...
};