当前位置:首页 > 文章列表 > 文章 > 前端 > 前端大列表滚动为什么会掉帧:虚拟列表、批量渲染与 60 FPS 验收

前端大列表滚动为什么会掉帧:虚拟列表、批量渲染与 60 FPS 验收

来源:17golang原创 2026-08-24 17:06:48 0浏览 收藏

不少人维护的商品后台订单列表,数据量从800行涨到12000行之后,最先出问题的往往不是接口响应变慢,而是滚动鼠标滚轮的时候,整个表格仿佛被按住了一样卡顿。打开Performance面板一看,满屏的长任务和频繁布局,真正拖慢交互的原因其实很简单:一口气把所有行全部交给DOM渲染,以及筛选结果变化时连续触发无间隔的渲染操作。

这类问题通常要同时收缩两个优化方向:屏幕可视区域附近只保留少量行,数据变化时按批次把任务逐步交给浏览器执行。虚拟列表用来压缩常驻DOM的总数量,批量渲染用来避免主线程被一次性占满;只改其中任意一处,想稳定守住60 FPS往往很难实现。

要点速览
  • 先用Performance面板录制滚动和筛选的两条基线,不要凭感觉盲目修改组件逻辑。
  • 固定行高的列表优先使用可精确计算的可视窗口,保留上下缓冲区域避免快速滚动时露白。
  • 大批量更新要拆分成小批次,每批执行完之后主动让出主线程控制权。
  • 最终验收要同时看长任务占比、掉帧比例和输入延迟三个维度,不能只参考平均渲染时间。

前端虚拟列表只渲染可视窗口并用上下缓冲减少 DOM 数量

先把掉帧拆成两个可测问题

滚动掉帧和筛选卡顿经常同时出现,但二者走的不是同一条处理链路。滚动场景下主要看每帧耗时有没有超过约16.7ms,筛选场景下则要看一次状态更新会不会生成超长任务。如果把两种场景混在一起调试,优化完之后很容易得到“滚动流畅了一点,搜索还是很卡”的错误判断。

场景重点指标先查什么
连续滚动掉帧、帧耗时、输入延迟可见 DOM 数量与布局次数
筛选刷新长任务、响应延迟单次更新行数与脚本耗时
快速跳页空白、抖动、内存缓冲区与节点回收

先在同一台机器、同一个数据集下录制10秒连续滚动操作。把“能流畅滚动”这种模糊感受改成可以横向对比的基线:比如可视区域显示30条、总数据12000条、滚动期间长任务出现次数和超过16.7ms的帧占比。这个结果只能对应当前实现的表现,不代表换浏览器后效果也完全一致,所以后续要固定验收环境。

虚拟列表如何把 DOM 数量压到可控范围

固定行高场景下,滚动容器的顶部偏移量可以直接换算出当前需要渲染的起始数据索引:

const rowHeight = 36;
const overscan = 6;
const start = Math.max(0, Math.floor(scrollTop / rowHeight) - overscan);
const visibleCount = Math.ceil(viewportHeight / rowHeight) + overscan * 2;
const end = Math.min(rows.length, start + visibleCount);

const visibleRows = rows.slice(start, end);
const offsetTop = start * rowHeight;

外层容器仍然要保留总高度,否则滚动条会因为只剩几十个节点而失去真实比例。常见做法是用一个占位层撑起 rows.length * rowHeight 的高度,再把当前窗口放到 offsetTop 处。上下各留几行缓冲,能覆盖滚轮事件和触控惯性之间的短暂空档。

这里别急着把行高写死成固定值。如果每行的高度会因为文本换行、图片加载或者展开面板动态变化,索引换算出来的结果会持续失真,最终表现为列表跳动、行重叠或者滚动还没到底就提前触发触底事件。遇到这种情况,应该先限制内容区域的布局规则,或者改成行高测量缓存机制,没有稳定的高度模型支撑,虚拟化只会把表面问题藏得更深,后续调试更麻烦。

筛选和刷新为什么还需要批量渲染

虚拟列表只是减少了最终需要留在页面上的节点总数,筛选操作本身仍有可能在主线程里同步处理12000条全量数据,接着一次性同步创建整批行节点。更稳妥的处理方案是先把筛选结果计算完成,再把显示更新拆分成多个小批次,主动让出浏览器处理输入事件和绘制的机会:

function appendInBatches(items, render, batchSize = 80) {
  let cursor = 0;
  function flush() {
    const next = Math.min(cursor + batchSize, items.length);
    render(items.slice(cursor, next));
    cursor = next;
    if (cursor 

批次大小也不是越小越好。同样的80行数据,在办公本上运行刚好在预算内,但如果单元格内部逻辑复杂,执行耗时还是可能超过单帧阈值。实际调试时可以在Performance面板里观察每批脚本的执行耗时,再把批次调整到16.7ms预算以内,同时预留出足够的绘制余量。如果数据过滤逻辑本身计算量很大,可以把纯计算逻辑移到Worker里处理,主线程只负责接收最终结果和维护可视窗口。

前端大列表筛选后分批更新主线程并在每批之间恢复响应

一次验收要同时看滚动、筛选和回退

  1. 固定12000行测试数据和浏览器窗口尺寸,录制连续滚动10秒的表现。
  2. 重复执行关键词筛选、清空筛选和快速拖动滚动条跳转到末尾的操作,记录过程中的长任务和输入延迟。
  3. 检查列表顶部、底部、快速拖动滚动条时会不会出现空白、抖动或者重复渲染行的问题。
  4. 临时关闭虚拟化功能做对照测试,确认优化收益来自节点数量精简和批量渲染策略,而不是测试数据集偶然变小带来的偏差。

不少开发者会把“滚动时超过16.7ms的帧占比明显下降、筛选过程中没有连续长任务、快速滚动全程无空白”作为第一道验收门槛,之后再去低端设备上做兼容测试。如果列表行高不稳定、用户频繁展开详情面板,或者列表总数据量并不大,用普通分页方案实现反而会比虚拟列表更容易维护。性能优化的终点从来不是代码写得更复杂,而是交互时延预算能被稳定复现和保障。

常见问题:虚拟列表上线前还要确认什么

虚拟列表一定比分页快吗?

不一定。需要连续浏览大量内容、频繁快速滚动的大列表场景更适合做虚拟化;用户按条件查询、单页只展示几十条的后台表格,用普通分页实现往往更简单稳定。

为什么虚拟列表滚动时会出现白块?

通常是缓冲行数量太少、渲染批次太大或者行高估算错误导致的。可以先适当增加上下缓冲的预渲染行数,再检查实际行高有没有被异步图片加载和内容换行操作改变。

批量渲染能不能直接用 setTimeout?

可以用来做粗粒度的主线程让出操作,但它的执行时机不会和下一帧绘制同步。如果需要紧贴浏览器的绘制节奏调整渲染逻辑,优先使用requestAnimationFrame,并重新评估每批任务的工作量。

怎么判断该上 Worker?

当筛选、排序或者数据转换本身占用大量CPU时间,而整个计算任务又不依赖DOM操作的时候,再考虑把逻辑放到Worker里运行;所有DOM更新操作仍然必须回到主线程执行。

把优化结果留成可复测的记录

最后要保存好测试时的数据规模、设备型号、浏览器版本、窗口尺寸、虚拟窗口参数、批次大小和三组核心指标。下次组件升级或者单元格内部逻辑变更时,直接用同一套测试流程回放验证,就能明确感知到性能变化是来自数据量改动、样式逻辑调整,还是渲染策略本身出现了退化。

版本声明
本文转载于:17golang原创 如有侵犯,请联系study_golang@163.com删除
CSS scroll snap 怎么做横向卡片滑动:scroll-snap-stop、触摸手势与键盘验收CSS scroll snap 怎么做横向卡片滑动:scroll-snap-stop、触摸手势与键盘验收
上一篇
CSS scroll snap 怎么做横向卡片滑动:scroll-snap-stop、触摸手势与键盘验收
Go context.WithCancelCause 怎么传递取消原因:错误链与测试验收
下一篇
Go context.WithCancelCause 怎么传递取消原因:错误链与测试验收
查看更多
最新文章
查看更多
课程推荐
  • 前端进阶之JavaScript设计模式
    前端进阶之JavaScript设计模式
    设计模式是开发人员在软件开发过程中面临一般问题时的解决方案,代表了最佳的实践。本课程的主打内容包括JS常见设计模式以及具体应用场景,打造一站式知识长龙服务,适合有JS基础的同学学习。
    543次学习
  • GO语言核心编程课程
    GO语言核心编程课程
    本课程采用真实案例,全面具体可落地,从理论到实践,一步一步将GO核心编程技术、编程思想、底层实现融会贯通,使学习者贴近时代脉搏,做IT互联网时代的弄潮儿。
    516次学习
  • 简单聊聊mysql8与网络通信
    简单聊聊mysql8与网络通信
    如有问题加微信:Le-studyg;在课程中,我们将首先介绍MySQL8的新特性,包括性能优化、安全增强、新数据类型等,帮助学生快速熟悉MySQL8的最新功能。接着,我们将深入解析MySQL的网络通信机制,包括协议、连接管理、数据传输等,让
    500次学习
  • JavaScript正则表达式基础与实战
    JavaScript正则表达式基础与实战
    在任何一门编程语言中,正则表达式,都是一项重要的知识,它提供了高效的字符串匹配与捕获机制,可以极大的简化程序设计。
    487次学习
  • 从零制作响应式网站—Grid布局
    从零制作响应式网站—Grid布局
    本系列教程将展示从零制作一个假想的网络科技公司官网,分为导航,轮播,关于我们,成功案例,服务流程,团队介绍,数据部分,公司动态,底部信息等内容区块。网站整体采用CSSGrid布局,支持响应式,有流畅过渡和展现动画。
    485次学习
查看更多
AI推荐
  • ljg-skills -
    ljg-skills
    ljg-skills 是李继刚开源的 AI 技能与提示词集合,面向大模型使用者整理了一批可复用的 prompt、角色设定和任务技能模板,适合用于学习提示词设计、搭建个人 AI 工作流和沉淀团队常用智能体能力。
    5225次使用
  • MELO音乐 - AI 音乐生成平台,支持多模态创作能力
    MELO音乐
    MELO音乐是一站式AI视频与音乐制作助手,对标suno, udio的高品质体验。提供伴奏生成、原创写词、无损导出、哼唱识曲、混音变声等全套音频与短视频编辑工具。无论是流行Kpop、电音说唱、民谣古风、摇滚儿歌还是商用轻音乐,MELO为你免费谱曲,轻松做同款!
    4730次使用
  • UniScribe - AI 免费在线音视频转文字平台
    UniScribe
    UniScribe 是一款 AI 音视频转文字与内容整理工具,支持上传音频、视频文件或粘贴 YouTube 链接,自动生成转写文本、摘要、思维导图和关键问题,并支持多格式导出,适合会议记录、课程学习、访谈整理和内容创作复盘。
    4681次使用
  • 剧云 - 免费 AI 智能中文剧本创作平台
    剧云
    剧云是专业中文剧本创作平台,安全稳定运行十余年,集成AI编剧、剧本医生审核、人物小传、剧情关系图、大纲编写、多人协作、Word导入导出、版权管控功能,数据安全防护,轻松高效创作剧本。
    4936次使用
  • 万象有声 - AI 一站式有声内容创作平台
    万象有声
    万象有声,一个专为有声创作者打造的新一代智能有声内容创作平台。平台提供专业的智能拆章、智能画本编辑、AI配音、AI生成音效、后期制作、智能对轨、智能审听等有声创作全流程工具,可以帮助创作者高效、低成本创作出引人入胜的有声作品。立即体验,让有声书制作更简单!
    4894次使用
微信登录更方便
  • 密码登录
  • 注册账号
登录即同意 用户协议隐私政策
返回登录
  • 重置密码