React 长列表性能实战:用 windowing 渲染十万条数据不卡顿
test12026-09-161 次阅读
问题:一次性渲染十万行会怎样
后台表格、日志面板、聊天记录经常需要渲染成千上万条数据。如果直接用 map 把整个数组渲染成 DOM,浏览器会瞬间创建几万个节点,主线程被布局和绘制占满,滚动掉帧、输入卡死。
核心矛盾在于:屏幕可视区域同时只能显示大约 20 到 40 行,其余几万行对用户来说是看不见的,却白白消耗了内存和渲染时间。
思路:只渲染看得见的那一段
虚拟列表(windowing)的原理是:用一个固定高度的滚动容器,根据 scrollTop 计算出当前可见的起始索引和结束索引,只把这一段数据渲染成真正存在的 DOM 节点,其余位置用上下两个「占位撑高」的空 div 顶住,保证滚动条比例正确。
最小可运行实现
下面是不依赖第三方库的实现,rowHeight 固定,逻辑足够清晰,可直接粘进项目里改。
import { useState, useRef, useCallback } from "react";
interface Row {
id: number;
name: string;
}
const ROW_HEIGHT = 36;
const VIEWPORT = 400; // 容器可视高度
const OVERSCAN = 4; // 上下多渲染几行,避免快速滚动白屏
function VirtualList({ rows }: { rows: Row[] }) {
const [scrollTop, setScrollTop] = useState(0);
const ref = useRef<HTMLDivElement>(null);
const total = rows.length;
const start = Math.max(0, Math.floor(scrollTop / ROW_HEIGHT) - OVERSCAN);
const visibleCount = Math.ceil(VIEWPORT / ROW_HEIGHT) + OVERSCAN * 2;
const end = Math.min(total, start + visibleCount);
const slice = rows.slice(start, end);
const onScroll = useCallback((e: React.UIEvent<HTMLDivElement>) => {
setScrollTop(e.currentTarget.scrollTop);
}, []);
return (
<div ref={ref} onScroll={onScroll}
style={{ height: VIEWPORT, overflowY: "auto", position: "relative" }}>
<div style={{ height: total * ROW_HEIGHT }}>
<div style={{ transform: `translateY(${start * ROW_HEIGHT}px)` }}>
{slice.map((r) => (
<div key={r.id} style={{ height: ROW_HEIGHT, lineHeight: `${ROW_HEIGHT}px` }}>
{r.name}
</div>
))}
</div>
</div>
</div>
);
}
// 使用
const data = Array.from({ length: 100000 }, (_, i) => ({ id: i, name: `第 ${i} 行` }));
export default function App() {
return <VirtualList rows={data} />;
}
关键细节
- OVERSCAN 必须加:快速滚动时浏览器一帧内可能跳过几十行,多渲染上下各几行能避免白屏闪烁。
- 用 transform: translateY 偏移而不是改 margin,走合成层,不触发重排。
- 行高固定时索引计算是常数级;如果行高不固定,需要维护位置缓存或用第三方库(react-window / react-virtualized)估算。
- 大数据源应当配合分页或无限滚动,虚拟列表解决的是「已有数组很大」的渲染问题,不是「一次性拉全量」的接口问题。
什么时候该上库
固定行高自己写就够了。遇到不定高、横向滚动、表格单元格合并、回收节点复用等复杂场景,直接上 react-window,它的 FixedSizeList 和 VariableSizeList 已经把测量和缓存都做好了,比手写的边界处理更稳。
T
test1
文章作者
问题:一次性渲染十万行会怎样 后台表格、日志面板、聊天记录经常需要渲染成千上万条数据。如果直接用 map 把整个数组渲...
- 分类
- 技术
- 发布时间
- 2026-09-16
- 字数
- 约 2178 字
- 阅读
- 1 次
