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

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

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

相关推荐
小羊没烦恼!几秒前
Office文件的奥秘——.NET平台下不借助Office实现Word、Powerpoint等文件的解析(完)
java·大数据·前端·网络·word·powerpoint·.net
风骏时光牛马7 分钟前
AI提示词工程:高效构建精准指令的方法论
前端
葡萄城技术团队16 分钟前
一个好的 BI Dashboard 到底应该怎么设计?
前端
晴天162 小时前
前端 postMessage 使用场景
前端·javascript·网络
zhanghaha13143 小时前
HTML系列教程:15_标签 全称 + 简写 超详细讲解(新手专用)
前端·html
zhanghaha13143 小时前
HTML系列教程:14_HTML 表单与输入框 <form>、<input> 零基础详解
java·前端·javascript
IMPYLH3 小时前
HTML 的 <samp> 元素
前端·网络·html
IMPYLH3 小时前
HTML 的 <script> 元素
前端·html
weixin_493503678 小时前
Vue3 前端生成 PDF:会员证书与活动签到表的三种打印方案与踩坑记录
前端·pdf·状态模式
尾善爱看海11 小时前
前端算法与手写题集
前端·算法