标准答案
- 先知道列表总数、容器高度和单项高度,计算总占位高度。
- 监听滚动容器的 scrollTop,计算 startIndex 和 endIndex。
- 只渲染 startIndex 到 endIndex 之间的可见项,并适当增加 overscan 缓冲。
- 用 transform 或 paddingTop 把可见项移动到正确位置,让滚动位置看起来连续。
- 固定高度版本比较简单,动态高度列表还需要高度缓存、测量和滚动位置修正。
题目解析
虚拟列表解决的是 DOM 数量过多导致的渲染和滚动性能问题。数据可以有一万条,但页面上真正需要存在的 DOM 只应该是视口附近几十条。
固定高度版本的关键公式很直接:startIndex 等于 scrollTop 除以 itemHeight 向下取整,visibleCount 等于 containerHeight 除以 itemHeight 向上取整。再加一点 overscan,可以减少快速滚动时的空白感。
动态高度是难点。因为每项高度不同,不能再用简单除法定位,通常要维护每项高度、前缀高度或位置缓存,并在测量后修正滚动偏移。
代码示例
下面是固定高度虚拟列表的核心计算逻辑,真实组件还要接入滚动事件和渲染框架。
JavaScript
function getVirtualRange(options) {
const {
scrollTop,
itemHeight,
containerHeight,
total,
overscan = 3
} = options
const visibleCount = Math.ceil(containerHeight / itemHeight)
const start = Math.max(0, Math.floor(scrollTop / itemHeight) - overscan)
const end = Math.min(total, start + visibleCount + overscan * 2)
const offsetTop = start * itemHeight
const totalHeight = total * itemHeight
return {
start,
end,
offsetTop,
totalHeight
}
}常见误区
- 只截取可见数据,但没有设置总占位高度,导致滚动条长度不对。
- 没有 overscan,快速滚动时容易看到空白。
- 把固定高度公式直接套到动态高度列表,滚动位置会越来越不准。