Virtual List 学习笔记

2018-03-26  本文已影响44人  VioletJack

看了 Furybean 的文章 《再谈前端虚拟列表的实现》 学习了下 Virtual List。记了些笔记,并且动手实践了一下,在此记录一下~

大数据列表优化方案

1. 列表中有N个高度相同的元素

2. 高度不同

3. 使用缓存

为了避免每次滚动列表都要反复计算,所以使用缓存将计算过的元素属性都缓存下来。
具体做法:将所有测量计算过得元素的高度和偏移量保存在一个对象中,优先从对象中获取,如果没有再去计算高度和偏移量。

4. 获取列表高度优化

其实计算整个列表高度需要遍历所有元素计算高度并求和,也是很消耗性能的。
解决方案:定义一个所有元素的高度预估值,即所未显示元素的平均高度。计算时根据缓存来判断两种情况,如果没有缓存直接使用 数量 * 预估高度;如果有缓存,计算方法就是 缓存的偏移量 + 剩余元素数量 * 预估高度

5. 优化已缓存搜索

缓存数据检索也需要消耗资源,如何将资源消耗最小化?
文章中使用的是二分法搜索~(又 GET 到一个新技能 :D )这里自己试着写个二分法试试:

    <div>
        <input id="input" type="text"/>
        <button onclick="middleSearch()">search</button>
        <span id="result"></span>
    </div>
    <script>
        let arr = []
        let i = 10000
        while(i--) {
            arr.push(i*2)
        }

        function middleSearch(){
            const value = parseInt(document.getElementById("input").value)
            if (value != 0 && !value) {
                return -1
            }
            let start = 0
            let end = arr.length - 1
            let middle
            while(start <= end) {
                middle = Math.floor((start + end)/2)
                if (value == arr[middle]) {
                    console.log(middle)
                    document.getElementById("result").textContent = middle
                    return middle
                } else if (arr[middle] > value) {
                    end = middle - 1
                } else {
                    start = middle + 1                    
                }
            }
            if (!middle) {
                middle = -1
            }
            console.log(middle)
            document.getElementById("result").textContent = middle
            return middle
        }
    </script>

注意,二分法的前提必须是有序的数组集合才可以这么查找。这种查找方式效率要比逐条查询快很多。

6. 优化未缓存搜索

这是一种指数查找的方式,具体方法文中只是给了个图片显示,仔细看了下源码,了解了一下指数查找方式。

exponentialSearch(scrollTop) {
  let bound = 1;
  const data = this.data;
  const start = this.lastMeasuredIndex >= 0 ? this.lastMeasuredIndex : 0;
  while (start + bound < data.length && this.getItemSizeAndOffset(start + bound).offset < scrollTop) {
     bound = bound * 2;
  }
  return this.binarySearch(
    start + Math.floor(bound / 2), 
    Math.min(start + bound, data.length), 
    scrollTop
  );
},

仔细看下逻辑其实并不难:

一些思考

最后作者也留下了几个思考的问题:

  1. 根据渲染结果动态的更新列表项的高度
  2. 数据更新后让缓存失效,并尽可能让失效缓存的范围最小

第一个问题不太理解,何谓 渲染结果
第二个问题呢,说下大概猜想:

  1. 既然是数据更新,那么通过数据监听、对比、差异化等方式将更新的数据的 index 找出来。
  2. 在 getItemSizeAndOffset 中获取缓存方法前判断 index 相对 data 数据是否更新,更新则不读取缓存而是重新计算,并且更新缓存内容。

一些问题

最后

看了下大牛的文章,收获还是蛮多的。不过还是停留在接受信息的阶段,最好能够在思考和理解之后输出点内容。

上一篇 下一篇

猜你喜欢

热点阅读