Background:
Pages contain a large amount of data, which takes a long time to render, affecting user experience. In page development, we sometimes encounter business situations where the list data cannot be paginated to load or can be paginated, but the current page data is very large (including many images). For this, we call this kind of list a long list. For example, in some kanban data or in some foreign exchange trading systems, the front end will display the user’s position status (gain, loss, income, etc.) in real time. At this time, the user’s position list generally cannot be pagined.
In the high Performance Rendering of 100,000 pieces of data (time sharding) article, we mentioned that we can use time sharding to render long lists, but this method is more suitable for the DOM structure of list items is very simple. This article shows you how to use virtual lists to load large amounts of data simultaneously
Usage scenarios
- You cannot load list data using pagination;
- It can be paginated, but the page contains a lot of data or contains a lot of image data;
- The amount of data in the drop-down list is large.
Implementation scheme
What is a virtual list?
Virtual list is actually an implementation of on-demand display, that is, rendering only the visible area, not rendering or partial rendering of the data in the non-visible area, so as to achieve extremely high rendering performance.
Let’s say we have 10,000 records that need to be rendered at the same timeThe visible area
The height of500px
, and the height of the list item is50px
At this point, we can only see 10 list items on screen at most, so we only need to load 10 items on the first rendering.
After first loading, and then analyzing when scrolling occurs, we can know by calculating the current scroll value at this time on the screenThe visible area
List items that should be displayed.
If the scroll occurs and the scroll bar is 150px from the top, we know that the list items in the visible area are items 4 through 13.
The implementation of the virtual list is actually loading only when the first screen loadsViewing area
List items needed in, dynamically computed when scrolling occursViewing area
List items within and willInvisible area
An existing list item was deleted.
- Calculate the current
Viewing area
Initial data index (startIndex
) - Calculate the current
Viewing area
End data index (endIndex
) - Calculate the current
Visible area
Data and render it to the page - To calculate
startIndex
The offset position of the corresponding data in the entire liststartOffset
And set it to the list
Because it’s justViewing area
So in order to maintain the height of the list container and trigger the normal scrolling, the Html structure is designed as follows:
<div class="infinite-list-container">
<div class="infinite-list-phantom"></div>
<div class="infinite-list">
<!-- item-1 -->
<!-- item-2 -->
<!-- ...... -->
<!-- item-n -->
</div>
</div>
Copy the code
infinite-list-container
为Viewing area
The containerinfinite-list-phantom
Is a placeholder in the container, height is the total list height, used to form scroll barsinfinite-list
Of the list itemApply colours to a drawing area
Then, listen for the Scroll event of infinite-list-Container to obtain the scroll position scrollTop
- Assume that
Viewing area
The height is fixedscreenHeight
- Assume that
List each
The height is fixeditemSize
- Assume that
The list of data
Call itlistData
- Assume that
Current scroll position
Call itscrollTop
Then it can be calculated that:
- Total list height
listHeight
= listData.length * itemSize - The number of list items that can be displayed
visibleCount
= Math.ceil(screenHeight / itemSize) - The initial index of the data
startIndex
= Math.floor(scrollTop / itemSize) - The end index of the data
endIndex
= startIndex + visibleCount - The data in the list is
visibleData
= listData.slice(startIndex,endIndex)
After scrolling, since the render area has been offset relative to the viewable area, I need to get an offset startOffset to offset the render area to the viewable area with style control.
- The offset
startOffset
= scrollTop – (scrollTop % itemSize);
The resulting simple code looks like this:
<template>
<div ref="list" class="infinite-list-container" @scroll="scrollEvent($event)">
<div class="infinite-list-phantom" :style="{ height: listHeight + 'px' }"></div>
<div class="infinite-list" :style="{ transform: getTransform }">
<div ref="items"
class="infinite-list-item"
v-for="item in visibleData"
:key="item.id"
:style="{ height: itemSize + 'px',lineHeight: itemSize + 'px' }"
>{{ item.value }}</div>
</div>
</div>
</template>
Copy the code
Export default {name:'VirtualList', props: {// All listData:{type:Array, default:()=>[]}, // Each height itemSize: {type: Number, default:200}}, computed:{// total listHeight listHeight(){return this.listdata.length * this.itemsize; }, visibleCount(){return math.ceil (this.screenheight/this.itemSize)}, ${this.startoffset}px,0) '; }, / / obtain real display list data visibleData () {return enclosing listData. Slice (enclosing start, Math. Min (this end, enclosing listData. Length)); } }, mounted() { this.screenHeight = this.$el.clientHeight; this.start = 0; this.end = this.start + this.visibleCount; }, data() {return {screenHeight:0, // offset startOffset:0, // start index start:0, // end index end:null,}; }, methods: {scrollEvent() {let scrollTop = this.$refs.list.scrollTop; This.start = math.floor (scrollTop/this.itemSize); This.end = this.start + this.visiblecount; This.startoffset = scrollTop - (scrollTop % this.itemSize); }}};Copy the code
We implemented a virtual list with text content dynamically pushing up list items, but we might find that the screen briefly went white when scrolling too fast.
To make the page scroll smoothly, we also need to render additional items above and below the visible area to give some buffer while scrolling, so we split the screen into three areas:
- Above the visible area:
above
- Visual area:
screen
- Below the viewable area:
below
Defining component propertiesbufferScale
, for receivingBuffer data
withViewable data
theThe proportion
Props :{// bufferScale:{type:Number, default:1}}Copy the code
AboveCount can be obtained as follows:
aboveCount(){
return Math.min(this.start,this.bufferScale * this.visibleCount)
}
Copy the code
BelowCount can be obtained as follows:
belowCount(){
return Math.min(this.listData.length - this.end,this.bufferScale * this.visibleCount);
}
Copy the code
The real rendering data visibleData can be obtained as follows:
visibleData(){
let start = this.start - this.aboveCount;
let end = this.end + this.belowCount;
return this._listData.slice(start, end);
}
Copy the code
We can also use some packaged components, such as InfiniteScroll
<InfiniteScroll initialLoad={false} pageStart={0} loadMore={loadFrequency === 0 && this.handleInfiniteOnLoad} hasMore={loading === 1 && hasMore} useWindow={false} pullDownToRefresh // loader={<div className="loader" key={0}>Loading ... </div>} >Copy the code
How virtual lists differ from lazy loading
Lazy loading and virtual list are actually a kind of implementation of delayed loading, with the same principle but slightly different scenarios:
- Lazy loading application scenarios tend to favor network resource requests, which solves the problem of long response time caused by excessive network resource requests.
- Virtual list is an optimization of long list rendering, to solve the problem of performance bottleneck caused by large amount of data rendering;
Online Demo and complete code:
Codesandbox. IO/s/virtualli…
The document reference: cloud.tencent.com/developer/a…