这段时间写了一个 Vue 的滚动组件:starkwang/vue-virtual-collection,现在正式宣传一下~
类似的 vue 列表滚动组件已经有好几个了,但一直没有针对瀑布流的 vue 滚动组件,也就是类似这样的滚动组件:
点击下面可以直接看 demo 👇:
vue-virtual-collectionstarkwang.github.io欢迎任何形式的 PR、Issue ~
使用
用起来非常简单:
import Vue from 'vue'
import VirtualCollection from 'vue-virtual-collection'
Vue.use(VirtualCollection)
然后就可以在你的代码中使用了,下面是一个简单的例子:
<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 渲染出来,所以性能上比渲染全量数据要快太多了。
用代码简略表示一下就是:
const DisplayCells = Cells.filter(isInViewPort)
但这样实现的话会有问题,如果遍历所有数据一个一个去计算 isInViewPort,在数据很多很多的时候,性能非常差(然而现在大部分 infinite scroll 组件都是这么做的= =)。
为了高效率地 viewport 中有哪些 Cell 需要渲染,我们需要改用“块渲染”的思想。我们可以定义一个“块”为 200 * 200 的正方形,所有与这个块有重叠的 Cell 都会在这个块中记录下来。
这些“块”被保存在一个 Map 中,当滚动发生时,我们只需要计算当前该展示哪些块的数据,然后去这些块中找到对应的 Cell 就可以了,而不需要去遍历所有的 Cell。
下面是一个画出来的例子:
此时,Map中记录的应该是:
{
"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 是如何实现的:
github.com/bvaughn/rea…github.com类似的思想同样可以用到列表、表格、格栅系统上。