yuxino / source

🌟 The source code analysis
3 stars 0 forks source link

长列表优化 react-tiny-virtual-list (二) - 功能分析 #9

Open yuxino opened 3 years ago

yuxino commented 3 years ago

先从核心的模块SizeAndPositionManager说起。打开一下查看。不是很多400多行。

image

getItemSizeAndPositionManager

先看公用函数getItemSizeAndPositionManager,看起来就是一个初始化的函数。

image

看完已经明白了如何初始化一个SizeAndPositionManager了。主要的三个选项是itemCountitemSizeGetter, estimatedItemSize分别对应了文档上提到的几个属性,estimated是估算的意思,其他的都比较好理解,顺带一提这文件的源码其实来自react-virtualized,也就是说看了这玩意约等于也看了一下react-virtualized的源码。

image

回到这个函数里,itemCount是100,estimatedItemSize是15,itemSizeGetter是一段函数,目前用意不明。看起来每次调用都会给itemSizeGetterCalls增加东西。固定返回数字10(意义不明)。

更新: itemSizeGetterCalls就是拿来计算真是高度的。estimatedItemSize是预测高度。

这个函数会返回两个值sizeAndPositionManager, itemSizeGetterCalls

findNearestItem

这是第一个测试我们先看它。

image

这个测视里并没有用到itemSizeGetterCalls,所以我们也不太好确定itemSizeGetterCalls目前是做什么的。这个测试的目的是测试寻找最近元素用的。仔细看了一下注释并不是最近元素而是nearest the specified offset也就是最近偏移量的元素位置

image

初步结论

image

理解困惑之后

image

说白了这俩就是在找中间值。比如元素高10,目标位置是100那么最接近这个文素的就是第11个元素了,但是如果是101,那么第11个元素本身就是完美符合最接近偏移量的元素了。

初略看了一下代码实现,用到了二分(binarySearch)和指数查找(exponentialSearch)。准备之后去看看。

getSizeAndPositionForIndex

这块看起来蛮多的来研究一下。从名字来看是获取对应index的size和positon用的。

image

测试用例比较长,截图比较麻烦,一部分一部分来看。

image

噢噢噢,突然明白了,这个不是次数,是对应的 index 。因为访问的是 0 号,所以数组里面会有个 0。

image

那感觉上就会是访问一次加一条。。。我们试着多访问几次。。然鹅事实不是这样的。重复的访问只会有一条。

并且如果输入比较大的数字的话,比如3,就会变成[0,1,2,3]这样的结构。这应该要拜读源码才能理解了这个现象。感觉是为了获取正确的偏移量,需要按序的计算出之前元素的偏移量才能得出对应元素的偏移量。这个操作可能是感觉不是特别花时间,但是是可以优化的点。只要算过3,再获取2和1的时候就不需要重新计算了。不过整个算法复杂度看了一下也就O(n)吧。

image

下面的新的例子也印证了这一点。

image

优化测试。

image

好了本轮用例完结。

getSizeAndPositionOfLastMeasuredItem

这是getSizeAndPositionForIndex衍生出来的东西,看起来是会把值缓存下来,记录元素对应的大小(size)和元素的实际偏移量(offset)。

image

显而易见的用例。下一个。

getTotalSize

先看源码吧,这个东西源码比较短。

image

Total size of all items being measured.This value will be completedly estimated initially. As items as measured the estimate will be updated. 先说结论这句话有帮助么,没啥帮助,还是看源码和测试用例更能理解,先说结论 totalSize 算的是可滚动的总大小,以下简称总大小。

计算公式是: lastMeasuredSizeAndPosition.offset + lastMeasuredSizeAndPosition.size + (this.itemCount - this.lastMeasuredIndex - 1) * this.estimatedItemSize

简化一下。我们知道lastMeasuredSizeAndPosition就是上个单测里的东西,表示最后测量元素,包含sizeoffset两个属性。以下我们简称为lastOffsetlastSizelastMeasuredIndex就是最后测量元素的索引itemCount就是列表的总数。estimatedItemSize就是预计大小。

那么屡一下。

lastOffset + lastSize + (this.itemCount - this.lastIndex - 1) * estimatedItemSize

对应中文:

最后一个元素的偏移量 + 最后一个元素的大小 + (总数 - 最后一个元素的位置 - 1) * 预计大小就是 总大小(totalSize) 了。

再简化一下:

总大小 = 最后一个元素的偏移量 + 最后一个元素的大小 + (剩余元素个数) * 预计大小

因为我们访问到最后元素的时候,说明我们此时渲染到了这个地方。也计算过前面的元素大小了。

image

因为这个特性我们可以推测,如果是动态算高度的性能会很差,因为必须真正的渲染出来再计算高度。但是真实情况如何,目前不清楚。

getUpdatedOffsetForIndex

这又是一个非常大的测试块,说明这一块的功能很重要。

image