javascript
import React, { useState, useEffect, useRef } from 'react';
const BigTextViewer = ({ text, chunkSize = 10000 }) => {
const [visibleChunks, setVisibleChunks] = useState([]);
const [currentIndex, setCurrentIndex] = useState(0);
const containerRef = useRef(null);
useEffect(() => {
if (currentIndex * chunkSize >= text.length) return;
const renderNextChunk = () => {
const start = currentIndex * chunkSize;
const end = Math.min(start + chunkSize, text.length);
const chunk = text.substring(start, end);
setVisibleChunks((prev) => [...prev, chunk]);
setCurrentIndex((prev) => prev + 1);
};
// 使用requestAnimationFrame进行分批渲染
if (currentIndex < 100) {
// 初始快速渲染
requestAnimationFrame(renderNextChunk);
} else {
// 后续慢速渲染,避免阻塞
setTimeout(renderNextChunk, 0);
}
}, [currentIndex, text, chunkSize]);
return (
<div ref={containerRef} className="min-h-full whitespace-pre-wrap p-4 border rounded">
{visibleChunks.map((chunk, index) => (
<span key={index}>{chunk}</span>
))}
</div>
);
};
export default BigTextViewer;
在 React 里把一个超大的字符串分块渲染出来,避免一次性渲染导致浏览器卡顿。
拆解一下逻辑 👇
📌 主要思路
-
分块切割字符串
- 用
chunkSize
(默认 10000 个字符)把大文本切成一段一段。 - 每次只渲染一段,而不是整个文本。
- 用
-
逐步渲染
- 用
requestAnimationFrame
在前 100 个分块时快速渲染(保证用户很快看到内容)。 - 超过 100 块后,改用
setTimeout
(异步队列)继续加载,避免主线程被长时间占用。
- 用
-
状态管理
visibleChunks
存储已经渲染的字符串块。currentIndex
记录当前渲染到第几块。
-
渲染输出
- 每个 chunk 作为一个
<span>
渲染,最终拼接成完整的大文本。 whitespace-pre-wrap
让空格和换行格式保持不变。
- 每个 chunk 作为一个
📌 执行过程
- 初始
currentIndex = 0
useEffect
检查是否还有没渲染的块 → 切下一块 → 更新状态 →currentIndex + 1
- 组件重新渲染 →
useEffect
再次运行 → 继续下一块 - 循环直到所有 chunk 渲染完。
📌 使用场景
- 大日志文件预览
- 代码文件(几十 MB)在线展示
- 逐步加载长文本(小说、爬虫抓取的长文章)
📌 优点
✅ 不会卡死页面(因为分批渲染)。
✅ 用户能很快看到前面的内容,而不是等完整数据。
✅ 可调 chunkSize
控制性能 vs 流畅度。
📌 局限
⚠️ 所有渲染出来的内容还是会一直留在 DOM 里,超大文本(几百 MB)会造成 DOM 数量过多,拖慢滚动性能。
👉 如果是这种情况,就得结合 虚拟列表 (react-window
/ react-virtualized
)来做。