技術概述
虛擬列表(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;
width: 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
列表的高度。當滾動方向為垂直時必填。 - width: 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