In Element-ui, the problem of slow rendering and page freezing caused by the large amount of options data for the selector (Select) is solved

Business scenario : The server returns more than 20,000 pieces of data and needs to be displayed in the drop-down box. Direct rendering will cause the page to freeze and take a long time to wait. The user experience is extremely poor, so we optimize this el-select and directly upload the code .

Relevant knowledge portal:
Vue implements custom directives and application scenarios
Vue implements anti-shake, throttling and application scenarios of functions

<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() {
    
    
      // 测试数据25000条数据, 这里数据多少条无所谓,options.slice(0, rangeNumber)方法只会默认加载初始的10条数据
      for (let i = 0; i < 25000; i++) {
    
    
        this.options.push({
    
    label: "选择"+i,value:"选择"+i});
      } 
    },
    loadMore(n) {
    
    
      // n是默认初始展示的条数会在渲染的时候就可以获取,具体可以打log查看
      // elementui下拉超过7条才会出滚动条,如果初始不出滚动条无法触发loadMore方法
      return () => (this.rangeNumber += 5); // 每次滚动到底部可以新增条数  可自定义
    },
  },
  beforeMount() {
    
    
    this.getList();
  },
  directives:{
    
    
    'el-select-loadmore':(el, binding) => {
    
    
      // 获取element-ui定义好的scroll盒子
      const SELECTWRAP_DOM = el.querySelector(".el-select-dropdown .el-select-dropdown__wrap");
      if(SELECTWRAP_DOM){
    
    
        SELECTWRAP_DOM.addEventListener("scroll", function () {
    
    
          /**
           * scrollHeight 获取元素内容高度(只读)
           * scrollTop 获取或者设置元素的偏移值,
           *  常用于:计算滚动条的位置, 当一个元素的容器没有产生垂直方向的滚动条, 那它的scrollTop的值默认为0.
           * clientHeight 读取元素的可见高度(只读)
           * 如果元素滚动到底, 下面等式返回true, 没有则返回false:
           * ele.scrollHeight - ele.scrollTop === ele.clientHeight;
           */
          const condition = this.scrollHeight - this.scrollTop <= this.clientHeight;
          if (condition) binding.value();
        });
      }
    },
  }
};
</script>

Finally, users of more than 20,000 pieces of data can’t keep scrolling to find the data they want. Remember to set the filterable attribute to realize the search function;
Insert picture description here
problem: if only the filterable attribute of element-ui is set, then the search scope is only lazy loading has been scrolled out Data, leading to incomplete and inaccurate searches.
Insert picture description here

In order to solve this problem, we continue to use the filter-method attribute and combine the visible-change event, as well as adding anti-shake optimization when searching input .

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
}
<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: {
    
     
    // 模拟获取大量数据
    getList() {
    
    
      // 测试数据15000条数据, 这里数据多少条无所谓,options.slice(0, rangeNumber)方法只会默认加载初始的10条数据
      for (let i = 0; i < 25000; i++) {
    
    
        this.resOptions.push({
    
    label: "选择"+i,value:"选择"+i});
      } 
    },
    loadMore(n) {
    
    
      // n是默认初始展示的条数会在渲染的时候就可以获取,具体可以打log查看
      // elementui下拉超过7条才会出滚动条,如果初始不出滚动条无法触发loadMore方法
      return () => (this.rangeNumber += 5); // 每次滚动到底部可以新增条数  可自定义
    },
    // 筛选方法
    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),
    // 下拉框出现时,调用过滤方法
    visibleChange(flag){
    
    
      if(flag){
    
    
        this.filterMethod()
      }
    },
  },
  beforeMount() {
    
    
    this.getList();
  },
  directives:{
    
    
    'el-select-loadmore':(el, binding) => {
    
    
      // 获取element-ui定义好的scroll盒子
      const SELECTWRAP_DOM = el.querySelector(".el-select-dropdown .el-select-dropdown__wrap");
      if(SELECTWRAP_DOM){
    
    
        SELECTWRAP_DOM.addEventListener("scroll", function () {
    
    
          /**
           * scrollHeight 获取元素内容高度(只读)
           * scrollTop 获取或者设置元素的偏移值,
           *  常用于:计算滚动条的位置, 当一个元素的容器没有产生垂直方向的滚动条, 那它的scrollTop的值默认为0.
           * clientHeight 读取元素的可见高度(只读)
           * 如果元素滚动到底, 下面等式返回true, 没有则返回false:
           * ele.scrollHeight - ele.scrollTop === ele.clientHeight;
           */
          const condition = this.scrollHeight - this.scrollTop <= this.clientHeight;
          if (condition) binding.value();
        });
      }
    },
  }
};
</script>

effect:
Insert picture description here

Guess you like

Origin blog.csdn.net/ZYS10000/article/details/111822862