Virtual List 学习笔记

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

大数据列表优化方案

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

  • 根据单个元素高度及元素数量计算整个列表高度,使用空白元素撑起列表高度。
  • 根据 ScrollTop 属性和元素高度计算从哪个元素开始显示,并通过显示容器的高度确定最后一个需要显示的元素,最后返回需要显示元素的数组。
  • 用vue渲染需要显示的列表,通过scrollTop确定top元素的位置。

2. 高度不同

  • 添加单个列表元素高度计算方法 itemSizeGetter,该方法返回单个列表元素的高度。
  • 使用 itemSizeGetter 方法求和计算整个列表高度。
  • 使用 ScrollTop 属性和 itemSizeGetter 方法计算第一个显示的列表元素的索引和偏移量。再配合显示容器的高度计算最后一个显示的列表元素的索引和偏移量。最后返回需要显示元素的数组。
  • 用vue渲染需要显示的列表,通过scrollTop确定top元素的位置。

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
  );
},

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

  • 先获取开始查询的位置 start,从 lastMeasuredIndex 开始查起。
  • 循环执行 getItemSizeAndOffset 方法获取偏移量与 ScrollTop 属性对比。如果偏移量小于 ScrollTop,则 bound 乘以2(其实就是指数加1),直到 start 索引值超出元素数量或者偏移量大于 ScrollTop,跳出循环。
  • 跳出循环说明在最后一个指数有我们需要的数据,获取跳出循环前的 start 索引(即除以2),最后还是使用二分法去找最终结果。

一些思考

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

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

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

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

一些问题

  • sizeAndOffsetCahce 缓存为何是对象而非数组,因为
  • 二分法查找数据中,返回 0 不返回 -1 是因为之后要用到 slice 方法的关系吧?如果为 -1 会查找数组后一位的数据。
  • 二分法查找数据中,if (low > 0){ index = low - 1 } 这段代码的意义何在?

最后

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

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,271评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,275评论 2 380
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,151评论 0 336
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,550评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,553评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,559评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,924评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,580评论 0 257
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,826评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,578评论 2 320
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,661评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,363评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,940评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,926评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,156评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,872评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,391评论 2 342

推荐阅读更多精彩内容

  • 问答题47 /72 常见浏览器兼容性问题与解决方案? 参考答案 (1)浏览器兼容问题一:不同浏览器的标签默认的外补...
    _Yfling阅读 13,725评论 1 92
  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 134,580评论 18 139
  • Android 自定义View的各种姿势1 Activity的显示之ViewRootImpl详解 Activity...
    passiontim阅读 171,389评论 25 707
  • 去年局地苹果、梨等农产品去库存大战还未结束,今年新一轮的果品销售已拉开帷幕。受这两年来频频出现的农产品滞销影响,不...
    农牧人阅读 321评论 0 1
  • 突如其来的表白, 时间一点点流逝。 默默不语转话题, 不是不喜欢你, 而是青春伤不起! 运动会仿若在即, 如果不曾...
    玫瑰花的梦阅读 129评论 6 4