Small knowledge, big challenge! This article is participating in the creation activity of “Essential Tips for Programmers”

This article also participated in the “Digitalstar Project” to win a creative gift package and creative incentive money

Business scenario: More than 20,000 pieces of data returned by the server need to be displayed in the drop-down box. Direct rendering will lead to page lag and require a long time to wait, resulting in poor user experience. Therefore, we optimize the el-select and directly upload the code.

Related knowledge portals:

Vue implements custom directives and application scenarios

Vue function to achieve the anti – shake, throttling and application scenarios

<template>
  <div class="content">
    <el-select v-model="chooseValue" filterable v-el-select-loadmore="loadMore(rangeNumber)">
      <el-option
        v-for="(item, index) in options.slice(0, rangeNumber)"
        :key="index"
        :label="item.label"
        :value="item.value">
      </el-option>
    </el-select>
  </div>
</template>
 
<script>
export default {
  data() {
    return {
      chooseValue: "".options: [].rangeNumber: 10}; },methods: { 
    getList() {
      The options.slice(0, rangeNumber) method only loads the initial 10 bytes by default
      for (let i = 0; i < 25000; i++) {
        this.options.push({label: "Choice"+i,value:"Choice"+i}); }},loadMore(n) {
      // n is the default initial display of the number of items can be obtained during rendering, specific can be viewed by typing log
      // The loadMore method cannot be triggered if the elementui drops more than 7 scrollbars
      return () = > (this.rangeNumber += 5); // The number of new items can be added to the bottom of each scroll}},beforeMount() {
    this.getList();
  },
  directives: {'el-select-loadmore':(el, binding) = > {
      // Get the Scroll box defined by element-ui
      const SELECTWRAP_DOM = el.querySelector(".el-select-dropdown .el-select-dropdown__wrap");
      if(SELECTWRAP_DOM){
        SELECTWRAP_DOM.addEventListener("scroll".function () {
          /** * scrollTop Gets or sets the element's offset. * Is used to calculate the position of the scrollbar when an element's container does not produce a vertical scrollbar. * clientHeight reads the visible height of the element (read only) * The following equation returns true if the element scrolls to the bottom, false if it does not: * ele.scrollHeight - ele.scrollTop === ele.clientHeight; * /
          const condition = this.scrollHeight - this.scrollTop <= this.clientHeight;
          if(condition) binding.value(); }); }}}};</script>
Copy the code

Finally, users with 20,000 pieces of data cannot scroll down to find the data they want. Remember to set the Filterable attribute to realize the search function.

Problem: If you set only the Filterable property of element-UI, the search scope will only be lazy loading of scrollout data, resulting in incomplete and inaccurate searches.

To solve this problem, we continued to use the filter-method attribute, combined with visible-change events, and added anti-shaking during search input for optimization.

Final code:

// utils.js
function _debounce(fn, delay = 300) {
  var timer = null;
  return function () {
    var _this = this;
    var args = arguments;
    if (timer) clearTimeout(timer); 
    timer = setTimeout(function () {
      fn.apply(_this, args);
    }, delay);
  };
}
export {
  _debounce
}
Copy the code
<template>
  <div class="content">
    <el-select 
      v-model="chooseValue" clearable filterable :filter-method="filterMethod" 
      v-el-select-loadmore="loadMore(rangeNumber)"
      @visible-change="visibleChange">
      <el-option
        v-for="(item, index) in options.slice(0, rangeNumber)"
        :key="index"
        :label="item.label"
        :value="item.value">
      </el-option>
    </el-select>
  </div>
</template>
 
<script>
import {_debounce} from '@/utils/index.js'
export default {
  data() {
    return {
      chooseValue: "".options: [].rangeNumber: 10.resOptions:[],
    };
  },
  methods: { 
    // Get a lot of data
    getList() {
      The options.slice(0, rangeNumber) method only loads the initial 10 bytes by default
      for (let i = 0; i < 25000; i++) {
        this.resOptions.push({label: "Choice"+i,value:"Choice"+i}); }},loadMore(n) {
      // n is the default initial display of the number of items can be obtained during rendering, specific can be viewed by typing log
      // The loadMore method cannot be triggered if the elementui drops more than 7 scrollbars
      return () = > (this.rangeNumber += 5); // The number of new items can be added to the bottom of each scroll
    },
    // Filter method
    filterMethod:_debounce(function(filterVal){
      if(filterVal){
        let filterArr = this.resOptions.filter((item) = >{
          return item.label.toLowerCase().includes(filterVal.toLowerCase())
        })
        this.options = filterArr;
      }else{
        this.options = this.resOptions; }},500),
    // Call the filter method when the drop-down box appears
    visibleChange(flag){
      if(flag){
        this.filterMethod()
      }
    },
  },
  beforeMount() {
    this.getList();
  },
  directives: {'el-select-loadmore':(el, binding) = > {
      // Get the Scroll box defined by element-ui
      const SELECTWRAP_DOM = el.querySelector(".el-select-dropdown .el-select-dropdown__wrap");
      if(SELECTWRAP_DOM){
        SELECTWRAP_DOM.addEventListener("scroll".function () {
          /** * scrollTop Gets or sets the element's offset. * Is used to calculate the position of the scrollbar when an element's container does not produce a vertical scrollbar. * clientHeight reads the visible height of the element (read only) * The following equation returns true if the element scrolls to the bottom, false if it does not: * ele.scrollHeight - ele.scrollTop === ele.clientHeight; * /
          const condition = this.scrollHeight - this.scrollTop <= this.clientHeight;
          if(condition) binding.value(); }); }}}};</script>
Copy the code

Effect: