在前端开发中,长列表渲染是高频场景:聊天记录、商品列表、日志展示、数据看板……当数据量达到成百上千甚至上万条时,直接渲染所有 DOM 会带来致命问题:
- 首次渲染白屏、卡顿
- 滚动掉帧、页面卡死
- DOM 数量爆炸,内存占用飙升
传统方案要么分页(体验差),要么懒加载(依然会堆积 DOM),而虚拟列表是解决长列表性能问题的最优解。
前几天看到了一个开源的Pretext库,于是就用纯原生 HTML + JavaScript,结合高性能文本计算库 Pretext,实现一套支持动态高度、零回流、上拉加载的高性能虚拟列表,代码可直接商用,十万条数据流畅滚动。
一、什么是虚拟列表?核心原理一句话讲清
虚拟列表 = 只渲染「可视区域 + 少量缓冲区域」的 DOM,其余内容用高度占位撑开滚动条。
它的核心逻辑非常简单:
- 计算所有列表项总高度,用一个空节点撑开容器滚动条(用户感知正常滚动)
- 监听滚动位置,计算当前可视区域应该显示哪些数据
- 只渲染这一小部分数据,滚动时动态更新 DOM
- 配合缓冲区,避免滚动时出现白屏
最终效果:无论 1 万条还是 10 万条数据,页面永远只渲染 20~30 个 DOM 节点。
二、本方案核心优势
相比市面上大多数虚拟列表,我们这个原生版本具备顶级性能:
纯原生无依赖:不依赖 Vue/React,直接运行
真正动态高度:支持文本自动换行,精准计算高度
零 DOM 回流:使用 Pretext 纯 JS 计算文本高度,不创建临时 DOM
滚动丝滑无白屏:内置上下缓冲区
上拉加载更多:无缝支持大数据量分页
移动端适配:支持流畅惯性滚动
代码极简可维护:核心逻辑不到 200 行 JS
三、关键技术点解析
1. 动态高度计算(最大痛点解决)
绝大多数虚拟列表只能用固定高度,因为动态高度需要创建 DOM 测量,触发昂贵回流。
我们使用 Pretext:
- 纯 JavaScript 实现文本布局
- 基于 Canvas 测量字符宽度 + 数学计算
- 不操作 DOM、不触发重排
- 支持中文/英文/混合文本自动换行
- 计算速度极快,适合大量数据
// 核心:纯 JS 计算列表项高度
function calcItemHeight(item) {
const prep = prepare(item.content, FONT)
const { height } = layout(prep, ITEM_WIDTH, LINE_HEIGHT)
return height + 24 // 文本高度 + 内边距
}
2. 高度缓存机制
- 首次加载统一计算所有项高度
- 存入数组缓存,避免重复计算
- 加载更多时增量缓存
- 总高度通过数组求和得出
3. 可视区域计算算法
- 从顶部累加高度,找到第一个出现在可视区域上方的索引
- 减去缓冲条数,得到起始渲染索引
- 继续累加,找到可视区域最下方的索引
- 加上缓冲条数,得到结束渲染索引
- 使用 slice 截取数据,只渲染可视部分
4. 定位优化:transform 替代 top
使用 translateY 移动渲染区域:
- 浏览器硬件加速
- 比修改 top 性能高 3~5 倍
- 滚动极致流畅
listWrapper.style.transform = `translateY(${offsetY}px)`
四、完整可运行代码 可直接访问
原生 JavaScript 高性能虚拟列表,十万数据秒级渲染不卡顿
五、适用场景
- 后台系统数据表格、日志列表
- 移动端聊天页、消息流
- 大数据看板、实时数据展示
- 商品列表、文件列表
- 任何需要展示超长列表的场景
六、性能对比(直观感受)
| 方案 | 1000 条 | 10000 条 | 100000 条 |
|---|---|---|---|
| 直接渲染 DOM | 轻微卡顿 | 严重卡顿 | 页面崩溃 |
| 普通虚拟列表 | 流畅 | 流畅 | 轻微掉帧 |
| 本方案(原生+Pretext) | 极流畅 | 极流畅 | 丝滑无感知 |
七、总结
虚拟列表不是高级技巧,而是前端长列表的标准最优实践。 这套原生 JavaScript 虚拟列表实现:
- 代码极简、易读易改
- 无框架绑定,可用于任何项目
- 动态高度支持,真正商用可用
- 十万数据不卡顿,性能达到业界一流水平
如果你正在面对长列表卡顿问题,直接复制代码即可解决 99% 的性能痛点。