Boost Transfer Component Performance with Lazy Loading and Infinite Scroll

Learn how to eliminate page lag when handling large datasets in Element UI's transfer component by applying lazy loading, infinite scroll, and pagination techniques, separating display from logic, and adding custom scroll handling for both downward and upward navigation.

WeDoctor Frontend Technology
WeDoctor Frontend Technology
WeDoctor Frontend Technology
Boost Transfer Component Performance with Lazy Loading and Infinite Scroll

Element Transfer Component Performance Optimization

Chen Jianbo: A Hearthstone player delayed by coding

Background

When the transfer component processes large amounts of data, rendering too many DOM nodes causes page lag. The goal is to improve performance while keeping the component's original logic largely unchanged.

Solution Idea

Apply lazy loading using an InfiniteScroll component. Copy the original component from packages/transfer (or repackage it as a private library) and add infinite‑scroll attributes.

v-infinite-scroll="pageDown"
:infinite-scroll-immediate="false"

Insert these attributes into the el-checkbox-group element:

<el-checkbox-group
  v-show="!hasNoMatch && data.length > 0"
  v-model="checked"
  :size="size"
  :class="{ 'is-filterable': filterable }"
  class="el-transfer-panel__list"
  v-infinite-scroll="pageDown"
  :infinite-scroll-immediate="false"
>
  <el-checkbox
    class="el-transfer-panel__item"
    :label="item[keyProp]"
    :disabled="item[disabledProp]"
    :key="item[keyProp]"
    v-for="item in filteredData"
  >
    <option-content :option="item"></option-content>
  </el-checkbox>
</el-checkbox-group>

Define pagination data in data:

pageSize: 20          // items per page
showData: []         // data displayed on the current page
filteredData: []     // data after filtering, used for rendering

Use v-for="item in showData" to render the visible items. v-for="item in showData"> Watchers handle data changes:

data(data) {
  const checked = [];
  this.showData = data.slice(0, this.pageSize);
  const filteredDataKeys = this.filteredData.map(item => item[this.keyProp]);
  this.checked.forEach(item => {
    if (filteredDataKeys.indexOf(item) > -1) {
      checked.push(item);
    }
  });
  this.checkChangeByUser = false;
  this.checked = checked;
},
filteredData(filteredData) {
  this.showData = filteredData.slice(0, this.pageSize);
}

Initialize the display count (e.g., 20 items).

Scrolling Down

Add a method that loads more items when the user reaches the bottom:

pageDown() {
  const l = this.showData.length;
  const totalLength = this.filteredData.length;
  if (l < totalLength) {
    this.showData = this.filteredData.slice(0, l + this.pageSize > totalLength ? totalLength : l + this.pageSize);
  }
}

This increases the displayed data length by 20 each time the user scrolls down, up to the maximum length, effectively solving the lag caused by large data sets.

New Issue

Manually scrolling to the end of the list and then performing a search still causes lag.

Advanced Optimization

During scrolling, the items above the viewport are not visible and do not affect user experience, so only the current page of 20 items needs to be shown. Add a ref="scrollContainer" to el-checkbox-group for scroll control and define a current page index:

data() {
  ...
  curIndex: 1
}

Modify pageDown to use curIndex:

pageDown() {
  const totalLength = this.filteredData.length;
  if ((this.curIndex * this.pageSize) < totalLength) {
    this.curIndex++;
    const targetLength = this.curIndex * this.pageSize;
    const endPoint = targetLength > totalLength ? totalLength : targetLength;
    const startPoint = endPoint - this.pageSize > 0 ? endPoint - this.pageSize : 0;
    this.showData = this.filteredData.slice(startPoint, endPoint);
    this.$refs.scrollContainer.$el.scrollTop = "1px"; // scroll to top of next page
  }
}

Since the built‑in InfiniteScroll directive only supports downward scrolling, add upward scrolling support:

mounted() {
  this.$refs.scrollContainer.$el.addEventListener('scroll', this.pageUp);
},
beforeDestroy() {
  this.$refs.scrollContainer.$el.removeEventListener('scroll', this.pageUp);
}

Implement pageUp to load the previous page when the scroll reaches the top:

pageUp(e) {
  if (e.target.scrollTop === 0 && this.curIndex > 1) {
    this.curIndex--;
    const endPoint = this.curIndex * this.pageSize;
    const startPoint = (this.curIndex - 1) * this.pageSize;
    this.showData = this.filteredData.slice(startPoint, endPoint);
    const el = this.$refs.scrollContainer.$el;
    el.scrollTop = el.scrollHeight - el.clientHeight - 1; // scroll to bottom of previous page
  }
}

Reset the scroll position and page index whenever the data changes to avoid unexpected pagination:

initScroll() {
  this.curIndex = 1;
  this.$refs.scrollContainer.$el.scrollTop = 0;
}

Call initScroll in the relevant watchers:

data() {
  ...
  this.initScroll();
  ...
},
filteredData(filteredData) {
  ...
  this.initScroll();
}

With these adjustments, the transfer component handles large data sets efficiently, eliminating lag while preserving the original component logic.

Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

PerformanceLazy Loadinginfinite scrollElement-UI
WeDoctor Frontend Technology
Written by

WeDoctor Frontend Technology

Official WeDoctor Group frontend public account, sharing original tech articles, events, job postings, and occasional daily updates from our tech team.

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.