虚拟滚动的核心是“滚前准备”,而非“边滚边算”:需预分块、建索引、worker 处理、缓存命中与缓冲区预热,主线程仅负责坐标计算与 dom 更新。

虚拟滚动本身不负责数据预处理,它只管“渲染哪几条”。海量数据块的前置处理必须在滚动发生前完成,否则滚动会卡顿、白屏甚至崩溃。关键不是“边滚边算”,而是“滚之前就准备好可直接渲染的结构”。
数据分块与索引预建
把原始数据按固定大小切片(如每500条为一块),同时生成一个轻量索引数组,每个元素记录该块的起始位置、长度、是否已加载等状态。这样滚动时只需用当前 scrollTop 除以 itemHeight,快速定位到目标块序号,再查索引取对应数据片段——避免每次滚动都遍历全量数组。
- 块大小建议设为 200–1000,太小增加索引开销,太大影响单次处理响应
- 索引数组本身应保持扁平,不要嵌套深层对象;可用 Uint32Array 存储起始偏移,节省内存
- 若原始数据带排序或过滤逻辑,这部分必须在分块前一次性完成,不要放到滚动事件里重算
计算密集型操作移交 Worker
如果数据块需要解析、格式转换、字段映射或正则清洗等 CPU 密集型操作,绝不能在主线程执行。用 Web Worker 将整块数据传入,在后台线程处理完毕后再 postMessage 回主线程,仅传递结果数组和块 ID。
Java项目代码review工具。分析Git变更+完整调用链路上下文,推断业务需求,进行多维度评分和分类汇总,生成完整PRD文档。包含细粒度Java代码审查清单(Null安全、异常处理、Streams、并发、equals/hashCode、资源管理、API设计、性能、MyBatis/ORM、事务边界、SQL/DD...
- 主线程只做坐标计算和 DOM 更新,Worker 负责“脏活”
- 使用 Transferable(如 ArrayBuffer)传递大数据,避免结构化克隆开销
- 可配合 requestIdleCallback 控制 Worker 启动节奏,避免抢占用户交互时机
缓存已处理块并支持快速跳转
用户可能快速拖动滚动条或点击时间轴跳转,这时不能等“从头加载”。需维护一个 LRU 缓存 Map,键为块 ID,值为处理后的数据数组 + 时间戳。缓存容量按内存预算设定(例如最多存 5 块),淘汰策略优先丢弃最久未用且非当前视口邻近块。
- 首次进入某块时触发处理,后续滚动回该区域直接命中缓存
- 缓存需标记“是否含动态字段”(如实时状态),对这类块设置较短过期时间
- 跳页操作(如月视图切换)应批量预取相邻 2–3 块,而非只取当前块
滚动前预热缓冲区
单纯渲染“刚好可见”的区域仍易出现滚动抖动。应在可视区域上下各扩展 1–2 块作为缓冲区,在用户尚未滚动到那里时,就提前触发对应块的加载与处理(前提是缓存未命中)。这个预热动作可通过 scroll 事件节流后触发,也可结合 IntersectionObserver 监听容器边缘。
- 缓冲区大小要权衡:太大加重初始负担,太小起不到平滑作用
- 预热任务应设优先级,正在滚动中时暂停非关键块处理,优先保障当前帧渲染
- 可利用 requestAnimationFrame 确保预热逻辑在下一帧空闲时段执行,不阻塞绘制
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南










