虚拟列表专为解决长列表卡顿而生,问题源于误用:滚动容器未设固定高度、干扰滚动样式的父级、itemSize传错或动态计算、itemCount误用items.length、position:sticky失效、新增数据不自动滚动、过度重算渲染逻辑。
虚拟列表不是“导致”长列表的问题,它就是专为解决长列表卡顿而生的——真正出问题的,是把虚拟列表当普通列表用,或者在不该用的地方硬套。
虚拟列表依赖可视区域精确计算起始索引,如果 #container 没设 height 或用了 min-height、overflow: visible,框架就无法知道“当前能看到几行”,只能退化成全量渲染或计算错乱。
div#list-container)设置明确的 height 或 max-height,且不能是 auto
transform、will-change、filter,否则 getBoundingClientRect() 偏移不准ref 绑定的容器需确保已挂载且尺寸稳定;React 中若用 useRef,别在 render 里反复调用 getBoundingClientRect()
FixedSizeList 要求每行高度严格一致,一旦传了错误值(比如把 48 写成 50),后续所有位置计算都会偏移;VariableSizeList 则要求 itemSize 函数必须返回确定值,且首次渲染后要缓存真实高度,否则反复调用拖慢滚动。
.list-item 的 height / line-height + padding 总和等于 itemSize 值itemSize 里直接读 DOM 高度,改用 useMemo 或外部 Map 缓存;首次渲染后立即用 measureRef(如 react-window 提供)更新缓存items.length 当作 itemCount 传入——必须传原始总条数,否则 filter 后索引错乱,新数据插入时定位失准因为虚拟列表靠 transform: translateY() 移动内容块,而 position: sticky 需要最近的非 transform 滚动祖先。此时 sticky 元素找不到有效锚点,自动降级为 relative。
立即学习“前端免费学习笔记(深入)”;
onScroll + scrollTop 手动控制其 top 和 z-index,加 requestAnimationFrame 防抖fixed 状态top 偏移,但要注意 iOS Safari 下 position: absolute 在 overflow-scroll 容器中仍可能跟随滚动(需测试)虚拟列表不会自动滚动到底部,哪怕你 push 了一条新消息到数组末尾,scrollTop 仍停在原位,用户得手动拉——这对聊天、日志等场景极不友好。
scrollTop + clientHeight >= scrollHeight - 1),再调用 scrollTo({ top: scrollHeight })
scrollIntoView({ block: 'end' }):它会触发 layout,频繁调用易卡顿;优先用 scrollTop = scrollHeight + requestAnimationFrame
最常被忽略的一点:虚拟列表的性能瓶颈不在“渲染多少”,而在“每次滚动时重算多少”。哪怕只渲染 5 行,如果 itemRenderer 里做了深 clone、正则匹配或未 memoized 的计算,照样卡。优化永远从单项开销开始,不是从总条数开始。