因为 DOM 性能瓶颈,大型列表存在难以克服的性能问题。 因此,就有了 “局部渲染” 的优化方案,这就是虚拟列表的核心思想。
虚拟列表的实现,需要重点关注的问题一有以下几点:
- 可视区域的计算方法
- 可视区域的 DOM 更新方案
- 事件的处理方案
下面逐一分解说明。
可视区域计算
可视区域的计算,就是使用当前视口的高度、当前滚动条滚过的距离,得到一个可视区域的坐标区间。 算出可视区域的坐标区间之后,在去过滤出落在该区间内的列表项,这个过程,列表项的坐标也是必须能算出的。
思考以下情况,
- 我们的视口高度为 100px
- 我们当前已经滚动了 100px
- 我们的列表项,每一项高度为 20px
根据这些条件,我们可以计算出,当前可视区域为第 11 项至第 20 项。
01 - 05,可视区域上方
+----+-----------+--------
| 06 | 100 ~ 120 |
+----+-----------+
| 07 | 120 ~ 140 |
+----+-----------+
| 08 | 140 ~ 160 | 可视区域
+----+-----------+
| 09 | 160 ~ 180 |
+----+-----------+
| 10 | 180 ~ 200 |
+----+-----------+--------
11 - N,可视区域下方
这是因为列表项高度是固定的,我们可以通过简单的四则运算算出已经滚动过去的 100px 中,已经滚动走了 5 个列表项,因此可视区域是从第 6 项开始,而视口高度为 100px,能容纳 100 / 20 即 5 个条目。
上面例子的情况非常简单,也不存在性能问题,因此实际上并没有展开讨论的价值。 而还有另一种复杂很多的情况,那就是,列表项高度不固定,根据内容决定高度。
此时,我们就没办法直接使用四则运算一步到位算出可视区域对应的条目了。
而必须实现一种机制,记录所有列表项的坐标,再通过检查列表项是否落在视口内。
下面重点讨论该问题。
列表项的坐标
列表项的坐标,可以通过以下公式定义:
<列表项 top 坐标值> = <上一项 top 坐标值> + <上一项的高度值>
第一个列表项的 top 坐标值为 0,因此,只要记录所有列表项目的高度,即可算出任意一个列表项的 top 坐标值。 于是,问题就变成了,必须使用某种方式来存储每个条目的高度。
我想,最容易想到的方案就是,使用一个数组,一一对应地存储列表每项的高度值。 然后获取特定项的坐标值时,提取第一项到目标项的值,进行累加运算。参考下面代码进行理解:
// 假设使用该数组存储列表每一项的高度 const itemHeightStore = [20, 20, 20, 20, 20] // 使用该方法,可以算出列表中指定项的 top 坐标值 const getTop = (index) => { let sum = 0 while (index--) sum += itemHeightStore[index] || 0 return sum } // 第一项 getTop(0) // 0 // 第二项 getTop(1) // 20 // ...
内容版权声明:除非注明,否则皆为本站原创文章。