【虚拟列表·终章】不定高度+动态图片加载,十万条数据流畅渲染全攻略!
大家好,我是 前端大卫。
在线 Demo 地址: codesandbox.io/p/devbox/ad…
今天是 虚拟列表 系列的终章,我将带大家深入探讨 不定高度列表项 的处理方式。如果你还没有看过前两篇内容,可以先点击下面的链接:
解决方案的优势
相较于市面上其他虚拟列表实现,我的这个方案具备以下优势:
-
高效索引查找
根据滚动方向精准定位起始和结束索引,显著提升性能。 -
创新高度调整机制
无需依赖传统二分查找算法,而是通过记录高度调整值,保证列表项的连续性。 -
动态监听高度变化
利用ResizeObserver
实时监听可视区域和整体列表高度的变化,确保数据准确。
如果你的项目不支持
ResizeObserver
,欢迎在评论区留言,我会单独出一篇文章讲解如何用其他技术解决监听问题。
接下来,我会通过实例,从 简单到复杂 手把手讲解解决方案,并分享一些重要注意事项。
核心实现步骤
1. 初始化数据结构
假设以下场景:
- 每项预估高度为 100
- 可视区域高度为 450
初始数据结构如下:
[
{ "top": 0, "bottom": 100, "height": 100 },
{ "top": 100, "bottom": 200, "height": 100 },
{ "top": 200, "bottom": 300, "height": 100 },
...
]
可以发现:
- 每项的
top
值为前一项的bottom
值。 - 每项的
bottom
值为自身top + height
。
2. 精确查找索引
根据滚动高度,确定起始和结束索引的公式为:
if (scrollTop >= item.top && scrollTop <= item.bottom) {
// 找到对应索引
}
注意:
列表项之间必须保持连续,否则会出现无法匹配的情况。例如,如果滚动高度为 140
,而列表项如下:
[
{ "top": 0, "bottom": 100 },
{ "top": 200, "bottom": 300 },
{ "top": 300, "bottom": 400 }
]
此时无法找到对应的索引,导致无法渲染虚拟列表。
3. 预估高度的作用
预估高度用于初始渲染,后续会根据实际高度进行调整。例如:
滚动高度为 0
,起始索引为 0
,结束索引为 4
,渲染如下:
4. 高度修正
渲染完成后,各列表项的实际高度可能不同。通过 ResizeObserver
,我们可以动态监听每项高度并修正:
- 如果列表项 高度较低:结束索引会增加,例如从
4
变为7
。
- 如果列表项 高度较高:结束索引会减少,例如从
4
变为3
。
5. 确保列表项连续性
这是实现的核心难点。未渲染的列表项需要与已渲染项保持连续,以下分两种情况讨论:
情况 1:高度连续
如果下一项的 top
大于上一项的 bottom
,简单赋值即可:
if (nextItem.top >= lastItem.bottom) {
nextItem.top = lastItem.bottom;
}
情况 2:高度不连续
如果下一项的 top
小于上一项的 bottom
,需要记录调整值:
const heightAdjustment = lastItem.bottom - nextItem.top;
无需逐项更新所有列表项,只需在需要时应用调整值即可。
6. 滚动方向的优化查找
根据 newScrollTop
和 prevScrollTop
,判断滚动方向:
- 向下滚动:新起始索引在旧索引下方。
- 向上滚动:新起始索引在旧索引上方。
利用方向判断,比二分查找效率更高。
7. 动态调整后的处理
当某项被删除或高度变化时,确保页面流畅性:
- 利用
uid
唯一标识复用旧列表项的数据。 - 根据规则修正:
top = previous.bottom
bottom = top + height
结语
代码细节可以查看我的 GitHub 项目,希望大家点个 ⭐ 支持!
GitHub 源码地址:
github.com/feutopia/fe…
如果你对虚拟列表有其他问题或建议,欢迎留言讨论!
最后
点赞👍 + 关注➕ + 收藏❤️ = 学会了🎉。
更多优质内容关注公众号,@前端大卫。