技术概述
虚拟列表(VirtualList)是一种在展示大量数据(长列表)时使用的插件,通过只显示必要的DOM和无限滚动,提升页面的性能。在web环境中,我们可以使用vue-virtual-scroll-list
之类的npm包。最近热门的小程序框架Taro3也提供了这个能力。从文档说明上看,其功能算是vue-virtual-scroll-list
的一个子集。
技术详述
在Taro中使用VirtualList非常简单,我们以Vue.js模式的项目为例——
这里是一个单词列表页面,需要展示数千个单词及中文,如果直接展示,页面的将会卡顿较长时间(团队成员也提出了这一点见issue),因此经过考虑我们选用了VirtualList作为解决方案,几乎不再会卡顿。以下是精简后的代码。
完整代码点此:Zhai-dict
1. 引入必要文件
// app.js 入口文件
import VirtualList from `@tarojs/components/virtual-list`
Vue.use(VirtualList)
2. 编写单项组件,用于长列表单个项目的展示
<! –– ListItem.vue 单项组件 ––>
<template>
<!-- 这里的style=css是一定要加的 -->
<view class="word-wrapper" :style="css">
<view class="word">{{ data[index].word }}</view>
<view class="translation">{{ data[index].translation }}</view>
</view>
</template>
<script>
export default {
name: 'ListItem',
props: ['index', 'data', 'css']
}
</script>
<style lang="scss">
.word-wrapper {
position: relative;
box-sizing: border-box;
100%;
padding: 30px 20px;
background: #f7f7f7;
border-bottom: 1px solid #dddddd;
...
}
传入该组件的props有以下4个属性:
- css: 单项的样式,样式必须传入组件的 style 中
- data: 组件渲染的数据,即virtualList的itemData属性
- index: 组件渲染数据的索引
- isScrolling: 组件是否正在滚动,当 useIsScrolling 值为 true 时返回布尔值,否则返回 undefined
通过这些属性可以渲染出单个的组件。
3. 编写长列表页面组件
<! –– History.vue 页面组件 ––>
<template>
<view id="pHistory">
<virtual-list
:height="listHeight"
:item-data="wordList"
:item-count="wordList.length"
:item-size="75"
:item="ListItem"
width="100%"
v-if="wordList.length"
:overscanCount="20"
/>
<view class="empty" v-show="wordList.length === 0">- 暂无内容 -</view>
</view>
</template>
<script>
import Taro from '@tarojs/taro'
import ListItem from './components/ListItem.vue'
export default {
name: 'pageHistory',
data() {
return {
pageState: 1,
wordList: [],
ListItem
}
},
computed: {
listHeight() {
return Taro.getSystemInfoSync().windowHeight - 50
}
},
}
</script>
virtualList的常用属性如下:
- item: VueComponent
将要渲染的列表单项组件,传入的props如上文所述。 - itemCount: number
列表的长度。必填。 - itemData: Array
渲染数据。必填。 - itemSize: number
列表单项的大小,垂直滚动时为高度,水平滚动时为宽度。必填。 - height: number | string
列表的高度。当滚动方向为垂直时必填。 - number | string
列表的宽度。当滚动方向为水平时必填。 - overscanCount: number = 1
在可视区域之外渲染的列表单项数量,值设置得越高,快速滚动时出现白屏的概率就越小,相应地,每次滚动的性能会变得越差。
注意事项&最佳实践
- 单项组件中,根元素一定要加上
:style="css"
,哪怕你没有传入自定义的css。没有加上会导致滚动之后页面显示不完全。 height
属性一定要是一个固定值,不能是百分比或vh/vw等。可以在computed
里面进行一系列处理,如History.vue32行。- 由于小程序的性能稍差,建议
overscanCount
设一个稍大的值,可能会有较好的展示效果。当然,过大的值也会产生反效果,这需要开发者进行一定尝试。
总结
本例的数据是从本地读取,因此也无需使用“无限滚动”的方案。虚拟列表组件和分页都是大量数据场景下提升性能的有效解决方案,在实际使用中有较大的意义。
从原理来看,这些组件都是通过计算好列表的长度,在wrapper处加上合适的padding-top和padding-bottom撑起这个列表,同时通过IntersectionObserver
等方式判断组件是否进入/离开了视口,并且只保留距离视口一定范围内的DOM元素。随着用户滚动不断更新展示的组件,这样就在用户无感的情况下做到了对超长列表的部分展示。
参考文档
https://github.com/tangbc/vue-virtual-scroll-list
https://nervjs.github.io/taro/docs/next/virtual-list/#itemcount-number-1