First, the use of background

In business development, there is a need for long list display. In the project, the chat list of some users will have thousands of data, which means that thousands of data will be rendered at one time, causing obvious lag. At this time, it is necessary to optimize the performance of chat list to improve user experience.

Lazy loading may be the first thing that comes to mind for many people with this requirement for performance optimization, but lazy loading has three fatal drawbacks for long lists:

  1. If you load all the way to the end, you’ll end up with a lot of DOM nodes, making scrolling not smooth.
  2. It can be very difficult to locate data in a particular location.
  3. The scrollbar does not correctly reflect the position of the information the operator is currently viewing in the overall list. And a lot of data loading, loading me a dozen, roll to the bottom too slow.

Lazy loading is not enough for a truly long list presentation, so what if you really want to solve such a problem? Another idea is list partial rendering, also known as virtual lists.

Two, virtual scrolling

Vue – virtual – scroller is introduced

The most popular third party libraries are VUE virtual-Scroller, React-Tiny-Virtual-List and React-Virtualized. Both of them can use partial loading to solve the problem of long lists. Vue -virtual-scroller, react-tiny-virtual-list and other solutions only support virtual lists. On the other hand, The Mode of react-Virtualized libraries is a mode of partial load that supports tables, sets, lists, etc.

Vue-virtual-scroller is an excellent framework.

Solutions to pure virtual lists such as vue-virtual-scroller and React-tiny-virtual-list. They work by using parallax and illusion to create a “virtual” list. A virtual list consists of three parts:

  1. Depending on the window
  2. Virtual data list (data presentation)
  3. Scroll placeholder block (bottom scroll area)

Begin to use

// Import vue from 'vue' import VueVirtualScroller in main.js from 'vue-virtual-scroller' import 'vue-virtual-scroller/dist/vue-virtual-scroller.css' Vue.use(VueVirtualScroller)Copy the code

Several commonly used components

The main components are RecycleScroller. Vue, DynamicScroller. Vue and DynamicScrollerItem.vue, and RecycleScroller is the core

What is the difference between RecycleScroller and Dynamic Roller?

In the application, the height of RecycleScroller item is static, that is, the height of each item in the list is consistent. DynamicScroller can be compatible with the height of item as dynamic. But theoretically RecycleScroller can also realize dynamic height item, as long as there is a scheme to calculate the height of item (DynamicScrollerItem is to solve this problem).

RecycleScroller and DynamicScroller common properties

Props attribute

Items: The list of items to display in the scrollbar, that is, the source data. Direction (Default: vertical) : vertical or horizontal ItemSize (default: NULL) : Displays in pixels the height (or width in horizontal mode) of the item used to calculate scroll size and position. If set to null (the default), it uses variable size mode. MinItemSize: The minimum size to use if the height (or width in horizontal mode) of the item is unknown. SizeField (default: “size”) : Field used to get the size of an item in variable-size mode. TypeField (default: “type”) : a field used to distinguish different types of components in a list. For each different type, a pool of reclaim items is created. KeyField (default: “ID”) : The field used to identify items and optimize the management render view. PageMode (default: false) : enable pageMode. Prerender (default: 0) : A fixed number of items are rendered for server-side rendering (SSR). Buffer (default: 200) : The number of pixels added to the edge of the scrolling visible area to start rendering items further away. EmitUpdate (Default: false) : An “Update” event is emitted each time the contents of the virtual scrollbar are updated (which may affect performance).

The event

Resize: Emitted when the scrollbar size changes. Visible: Emitted when the scroll bar thinks it is visible in the page. Hidden: Emitted when the scroll bar is hidden in the page. Update (startIndex, endIndex) : Emitted each time a view is updated, only if emitUpdate prop is true

Default scope slot value

Item: The item rendered in the view. Index: indicates the position of each item in the item array. Active: Indicates whether the view is active. The active view is considered visible and RecycleScroller is positioned by. Inactive views are not considered visible and are hidden from the user. If the view is inactive, any rendering related calculations should be skipped.

DynamicScrollerItem Common property (this component can only be used in DynamicScroller components)

Props attribute

Item (required) : Item rendered in the scroller. Active (Required) : is an active hold view in the loop wheel. Will prevent unnecessary size recalculation. SizeDependences: Value that may affect the size of the project. The value is monitored, and if a value changes, the size is recalculated. This is recommended instead of watchData. WatchData (default: false) : Drill down on the change items to recalculate the size (not recommended, may affect performance). Tag (default: “div”) : The element used to render the component. EmitResize (default: false) : Resizing events are emitted each time a recalculation occurs (which may affect performance).

RecycleScroller Use case

<template>
  <RecycleScroller
    class="scroller"
    :items="list"
    :item-size="32"
    key-field="id"
    v-slot="{ item }">
    <div class="user">
      {{ item.name }}
    </div>
  </RecycleScroller>
</template>
<script>
export default {
  props: {
    list: Array,
  }
}
</script>
<style scoped>
.scroller {
  height: 100%;
}
.user {
  height: 32%;
  padding: 0 12px;
  display: flex;
  align-items: center;
}
</style>
Copy the code

DynamicScroller use cases

<template> <DynamicScroller :items="items" :min-item-size="54" class="scroller"> <template v-slot="{ item, index, active }"> <DynamicScrollerItem :item="item" :active="active" :size-dependencies="[item.message]" :data-index="index"> <div class="avatar"> <img :src="item.avatar" :key="item.avatar" alt="avatar" class="image"> </div> <div class="text">{{ item.message }}</div> </DynamicScrollerItem> </template> </DynamicScroller> </template> <script> export default { props:  { items: Array, }, } </script> <style scoped> .scroller { height: 100%; } </style>Copy the code

3. Problems encountered in the actual use process

  1. At the beginning, RecycleScroller component was used, and problems were found in the display, which was found because the height of each item in the chat list was dynamically controlled by @media. If the size was reduced, the height display problem would occur, and the problem was solved after changing to DynamicScroller. DynamicScroller supports dynamic change of height and width for each item.
  2. In one scenario, the user clicks on an item in the chat list, the item becomes selected, and when the page is refreshed, the user needs to navigate to the selected item. Since virtual scrolling does not display all the data at once, it is obviously not feasible to start by matching to the current element and positioning it with a selector. You can now do this by finding the position of the element in the entire source data, and then calculating the sum of the heights of each term preceding that position. Change the scrollTop of the scrolling element to this value.

Four, source code interpretation and simple implementation

Framework implementation Idea

The main source logic in RecycleScroller. Vue file, the main implementation principle is through this. UpdateVisibleItems () method to calculate startIndex, endIndex to obtain the element array to render. Scroll: {start: XXX, end: XXX}. Calculate startIndex from viewport viewport range… EndIndex The style value (offset value) of each element, which is controlled by CSS3’s transform. The code is: style=”ready? { transform: translate${direction === ‘vertical’ ? ‘Y’ : ‘X’}(${view.position}px)} : null” So this is where you need some algorithmic knowledge. In the process of realizing the source code, the dichotomy method is used to improve the execution efficiency of the program.

DynamicScroller. Vue and DynamicScrollerItem.vue code. See the code we can find that dynamic Roller implementation also depends on RecycleScroller. Vue. The height/width of each item is obtained by dynamicScrollerItem. vue to obtain the size of the array element, and then revert to the corresponding realization of RecycleScroller.

Simple implementation

The following code is the simplest version of virtual scrolling by hand:

<div> <input type="text" v-model.number="dataLength"> <div class="virtual-scroller" @scroll="onScroll" :style="{height: 600 + 'px'}"> <div class="phantom" :style="{height: this.dataLength * itemHeight + 'px'}"> <ul :style="{'margin-top': `${scrollTop}px`}"> <li v-for="item in visibleList" :key="item.brandId" :style="{height: `${itemHeight}px`, 'line-height': `${itemHeight}px`}"> <div> <div>{{item.name}}</div> </div> </li> </ul> </div> </div> </div> </template> <script> export default { name: "vue-virtual-scroller", data() { return { itemHeight: 60, visibleCount: 10, dataLength: 500000, // total quantity startIndex: 0, endIndex: 10, scrollTop: 0}}, computed: { dataList() { const newDataList = [...Array(this.dataLength || 0).keys()].map((v, i) => ({ brandId: i + 1, name: '${I + 1}', height: this.itemheight}); return newDataList }, visibleList() { return this.dataList.slice(this.startIndex, this.endIndex) } }, methods: { onScroll(e) { const scrollTop = e.target.scrollTop this.scrollTop = scrollTop console.log('scrollTop', scrollTop) this.startIndex = Math.floor(scrollTop / this.itemHeight) this.endIndex = this.startIndex + 10 } } } </script> <style scoped> .virtual-scroller { border: solid 1px #eee; margin-top: 10px; height: 600px; overflow: auto } .phantom { overflow: hidden } ul { background: #ccc; list-style: none; padding: 0; margin: 0; } li { outline: solid 1px #fff; } </style>Copy the code

Reference documents: github.com/Akryum/vue-… Zhuanlan.zhihu.com/p/164699971 www.geekschool.org/2021/02/20/…

You may want to consider using virtual scrolling when you encounter similar performance optimizations for long lists in real business development.