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

转载原文出自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 是如何实现的:

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

相关推荐
我材不敲代码2 小时前
Python 函数核心:位置参数与关键字参数详解
java·前端·python
Kratzdisteln2 小时前
【无标题】
前端·python
Curvatureflight3 小时前
前端国际化 i18n 落地实践:语言包、动态文案和格式化问题怎么处理?
前端·c++·vue
kTR2hD1qb3 小时前
Claude Code Skill的介绍与使用
java·前端·数据库·人工智能
修己xj4 小时前
打造专属博文封面神器:一个开源免费的博文封面生成器ThisCover
前端
kyriewen4 小时前
面试8家前端岗位后,我发现了一个残酷的事实:AI不是加分项,是门槛
前端·javascript·面试
Fighting_p4 小时前
【面试 - el-select问题及解决】wujie 微前端下子系统 el-select 多选 filterable 过滤失效
前端
吃口巧乐兹4 小时前
AI 全栈时代,为什么要服务端使用 NestJs
前端
yingyima4 小时前
Redis 延迟任务队列:凌晨3点服务器报警的救星
前端
weiggle4 小时前
第三篇:可组合函数(Composable)——Compose 的基石
android·前端