虚拟滚动
Virtual Scrolling
列表只渲染看得见的那几行,一万条也能滚得顺。
- 又叫:
- 虚拟列表
- 长列表优化
- 窗口化渲染
- virtual list
- windowing
- virtualized list
- 展厅
- 布局与结构
- 形态
- 自带滚动
- 播放
- 手动触发
- 关键词
- 需要 JS / 浏览器 API / 合成层动画 / 会引发重排 / 主线程每帧 / 可以直接用
参数工作台
复制给 AI
随参数实时更新做一个虚拟滚动列表(virtual scrolling):总条数 10000,每行固定 44px,容器内放一个 10000 × 44px 的占位撑出滚动条,只渲染可视区行数加上前后各 4 行,用 transform: translateY 把渲染块定位到对应位置。scroll 里用 requestAnimationFrame 节流,只在可视区间变化时才更新;行用 aria-setsize / aria-posinset 报出总数与序号。
virtual scrolling list, 10000 rows of 44px fixed height, spacer of total x itemHeight to keep the scrollbar honest, render only the visible window plus 4 overscan rows, position the window with transform: translateY, throttle scroll with requestAnimationFrame and only re-render when the window changes, aria-setsize / aria-posinset on rows
https://publieople.github.io/design-museum/entry/virtual-scrolling/?stage=light
链接带着你调好的参数
什么时候用
- 几百到几万行的列表、日志、表格,全渲染会让页面卡死
- 聊天记录、时间线这类只往两头长的数据
- 可视区高度固定的面板里展示很长的候选列表
适用场景:列表 / 滚动 / 页面
推荐取值
- 行高
- 固定 40–64px 最省事
- 缓冲行数
- 前后各 3–6 行
- 滚动处理
- requestAnimationFrame 节流,或只监听可视区间变化
- 占位高度
- 总行数 × 行高
- 无障碍
- aria-setsize / aria-posinset 报出总数与序号
别搞混
和「无限滚动」的区别
无限滚动管「什么时候再取一批数据」,虚拟滚动管「一屏只渲染几行」;一个解决数据量,一个解决 DOM 量,经常一起用。
和「分页」的区别
分页把数据切成页、用户翻页;虚拟滚动数据一次拿全,只是把可视区外的行不渲染,滚动条仍然是连续的一整条。
容易踩的坑
- 行高固定才好算区间;高度不定就得逐行测量并缓存,成本随之上升
- 滚动容器要有撑满总高度的占位(spacer 或 padding),否则滚动条与位置全不对
- scroll 事件里直接 setState 会掉帧,用 requestAnimationFrame 节流,或者只在可视区间变化时才更新
- 可视区外多渲染几行做缓冲(overscan),否则快速滚动会看到空白
- 复用 DOM 时要清掉上一行的动画与展开状态,别让第 500 行的状态串到第 12 行
- 读屏软件看不到没渲染的行:用 aria-setsize / aria-posinset 报出总数与位置,或提供「跳到第 N 条」
无障碍降级
虚拟滚动本身不播动画;只有「滚到第 N 条」这类平滑滚动在 prefers-reduced-motion 下要改成瞬间定位。
关键词
- virtual scrolling
- windowing
- virtual list
- overscan
- item height
- scrollTop
- transform: translateY
- aria-setsize