基于虚拟块高效解决不定高虚拟列表

转载原文出自zhuanlan.zhihu.com/p/34380557 作者知乎 Starkwang

这段时间写了一个 Vue 的滚动组件:starkwang/vue-virtual-collection,现在正式宣传一下~

类似的 vue 列表滚动组件已经有好几个了,但一直没有针对**瀑布流**的 vue 滚动组件,也就是类似这样的滚动组件:

点击下面可以直接看 demo ?:

欢迎任何形式的 PR、Issue ~


使用

用起来非常简单:

js 复制代码
import Vue from 'vue'
import VirtualCollection from 'vue-virtual-collection'

Vue.use(VirtualCollection)

然后就可以在你的代码中使用了,下面是一个简单的例子:

html 复制代码
<template>
    <div>
        <VirtualCollection :cellSizeAndPositionGetter="cellSizeAndPositionGetter" :collection="items" :height="500" :width="330">
            <div slot="cell" slot-scope="props">{{props.data}}</div>
        </VirtualCollection>
    </div>
</template>

<script>
    export default {
        data () {
            return {
                /**
                 * This will create 1000 items like:
                 * [
                 *   { data: '#0' },
                 *   { data: '#1' },
                 *   ...
                 *   { data: '#999' }
                 * ]
                 */
                items: new Array(1000).fill(0).map((_, index) => ({ data: '#' + index }))
            }
        },
        methods: {
            cellSizeAndPositionGetter(item, index) {
                // compute size and position
                return {
                    width: 100,
                    height: 150,
                    x: (index % 2) * 110,
                    y: parseInt(index / 2) * 160
                }
            }
        }
    }
</script>

实现

现在自测大概可以无卡顿支持 100W+ 的数据渲染,大部分场景下肯定是够用了。原理上就是局部渲染DOM 回收,不会渲染全部数据,而是把当前 viewport 中展示的 Cell 渲染出来,所以性能上比渲染全量数据要快太多了。

用代码简略表示一下就是:

js 复制代码
const DisplayCells = Cells.filter(isInViewPort)

但这样实现的话会有问题,如果遍历所有数据一个一个去计算 isInViewPort ,在数据很多很多的时候,性能非常差(然而现在大部分 infinite scroll 组件都是这么做的= =)。

为了高效率地计算 viewport 中有哪些 Cell 需要渲染,我们需要改用"块渲染"的思想。我们可以定义一个"块"为 200 * 200 的正方形,所有与这个块有重叠的 Cell 都会在这个块中记录下来

这些"块"被保存在一个 Map 中,当滚动发生时,我们只需要计算当前该展示哪些块的数据,然后去这些块中找到对应的 Cell 就可以了,而不需要去遍历所有的 Cell。

下面是一个画出来的例子:

此时,Map中记录的应该是:

js 复制代码
{
  "0.0": [1, 2, 3, 5], // 0.0块与1,2,3,5号Cell有重叠,下同
  "0.1": [5, 3, 6, 7],
  "0.2": [7, 6, 8, 9],
  "1.0": [2, 3, 4],
  "1.1": [3, 4, 6],
  "1.2": [6, 9]
}

当我们滚动了页面,根据滚动的距离、viewPort 的宽高,可以很容易计算出当前需要渲染哪些块

比如上面这个例子中,我们需要渲染 0.0、0.1、1.0、1.1 这四个块,然后我们只需要去 Map 中找到这些块包含的 Cell,就可以高效率地渲染了,而不是去遍历所有的 Cell 暴力搜索。

这也是另一个十分流行的开源组件 react-virtualize 的核心思想,可以在下面看具体的 SectionManager 是如何实现的:

类似的思想同样可以用到列表、表格、格栅系统上。

相关推荐
郭老二3 小时前
【前端】vue3:编译步骤
前端
不在逃避q4 小时前
Trae/Vs Code/Cursor命令行无法跑npm命令
前端·arcgis·npm
夏殇之殁4 小时前
包中创建自定义列表项。 . 使用自定义列表项进行数据绑定 . 将天气预报数据保存到本地内存表,通过LiveBindings进行显示。 ...
服务器·前端·javascript
The Chosen One9854 小时前
高进度算法模板速记(待完善)
java·前端·算法
陈随易4 小时前
MoonBit抓包模块pcap,查看电脑的每一次联网通信
前端·后端·程序员
新中地GIS开发老师5 小时前
WebGIS开发学生作品|低空航天管理与航线规划系统
前端·javascript·webgis·三维gis开发
用户059540174466 小时前
大模型对话记忆持久化踩坑实录:用 Playwright 自动化测了 300 次,终于揪出会话丢失的真凶
前端·css
丙氨酸長鏈6 小时前
Web前端入门第 问:JavaScript 一个简单的 IndexedDB 数据库入门示例
前端·javascript·数据库
kyriewen7 小时前
面试官让我手写虚拟列表——AI生成的版本,快速滚动几下就白屏了
前端·javascript·面试
IT_陈寒7 小时前
Vite热更新失效?你可能漏了这个配置
前端·人工智能·后端