简单处理el-select加载大量数据时优化

103 阅读2分钟

动画.gif

功能说明:每次加载10条数据,数据量限制在 30条;

  • 包版本:
    • "element-ui": "^2.15.12",
    • "vue": "^2.6.14",
  • 文件说明,
    • banks.js,数据文件
    • dataLoader.js 数据加载器,核心功能
    • elSelectMassData.vue,组件

1. banks.js,数据文件

const MaxLen  = 100;
var banks = [];
for(let i=0;i<MaxLen;i++){
    banks.push({
        text:"银行"+(i+1),
        value:"id-"+i
    })
}
export default banks;

2. dataLoader.js,加载器

  • 文件,dataLoader.js 数据加载器,核心功能
    • getNext(),获取下一片数据,只是在初始化使用一下
    • getLast(),获取上一片数据,不向外暴露
const STATUS = {
  Top:'top',
  Bottom:'bottom',
}

class DataLoader {
  
  constructor(){
    this.data = [];   //数据
    this.len = 0;     //数据长度;

    this.indexStart = 0; //开始游标;
    this.indexEnd = 0;   //结束游标;

    this.result = [];   //结果集;
    this.resultSize = 30; //结果集所能存储的容量大小;
    
    //(这个没用,后面有时间可以优化,因为是引用类型所以不用也可以生效)
    this.callback = null; //回调函数;

    this.ele = null; //容器元素;

    this.status = STATUS.Top; // top | bottom ; 顶部,底部; STATUS
    this.config = {
      total:100,  //每次加载条数
      // isLog:true, //是否打印日志
    }
  }

  init(data,config={}){
        
    this.updateData(data);
    this.updateConfig(config)
    if(!this.ele){
      throw new Error('请设置容器元素');
    }
  }

  updateData(data){
    this.data =  JSON.parse(JSON.stringify(data));
    this.len = this.data.length;
  }

  updateConfig(config){
    
    Object.keys(config).forEach((key)=>{
      if(key != 'ele'){
        this.config[key] = config[key]
      }
    })

    if(config.ele){
      this.removeOnScroll();
      this.ele = config.ele;
      this.addOnScrollEvent();
    }
  }

  addOnScrollEvent(){
    console.log(this);
    this.ele.addEventListener('scroll',this.onScroll.bind(null,this));
  }
  removeOnScroll(){
    this?.ele && 
    this.ele.removeEventListener('scroll',this.onScroll.bind(null,this));
  }
  onScroll(that,e){
      const { scrollHeight, scrollTop, clientHeight} =  e.target;

      // 滚动到顶部
      if(scrollTop  <= 0){
        console.log('--top');
        that.getLast();
      }
      // 滚动到底部(上取整的目的是,有一个盒子问题, win缩放比)
      else if(scrollHeight - Math.ceil(scrollTop)  <= clientHeight){
        console.log('--bottom');
        that.getNext();
      }
  }
  
  getNext(){

    // 已经到达底部(在下面设定)
    if(this.status == STATUS.Bottom){
      console.warn('已经到达了底部');
      return
    }

    if(this.status == STATUS.Top) this.status = null;

    let data = [];
    const startIndex = this.indexEnd;
    this.indexEnd = this.indexEnd + this.config.total;
    
    //如果游标小于 数据长度
    if(this.indexEnd < this.len){
      data = this.data.slice(startIndex,this.indexEnd)
    //否则,游标大于数据长度
    }else{
      data = this.data.slice(startIndex);
      this.indexEnd = this.len;
      this.status = STATUS.Bottom
    }
    
    this.result.push(...data);

    if(this.result.length > this.resultSize){
      const delArr =  this.result.splice(0,this.result.length-this.resultSize);
      if(delArr.length){
        
        this.indexStart += delArr.length;
        console.log('++[indexStart]:',delArr.length, this.indexStart);
      }
      const { scrollHeight, clientHeight} =  this.ele;

      setTimeout(()=>{
        //30个元素,每个元素的高度;
        const size = scrollHeight/this.resultSize;
        //在可视区域里面,有多个元素可存在,上去整;
        const clientEleCount = Math.ceil(clientHeight/size);
        // 减去 增加的10个,减去可视区域的10个;计算出剩余的top;
        const count = this.resultSize - (this.config.total) - clientEleCount;
        //最终结果,再进行偏移量处理;
        this.ele.scrollTop = parseInt(count * size + 18)
      },0)
    }

    return {
      status:this.status,
      data:this.result
    }
  }
  getLast(){
    
    // 已经到达顶部
    if(this.status == STATUS.Top){
      console.warn('已经到达了顶部')
      return;
    }
    if(this.status == STATUS.Bottom) this.status = null;

    let data = [];
    let startIndex = this.indexStart - this.config.total; // test 11 | 2, | 0 , 10
    //设置禁止
    if(startIndex < 1){
      this.status = STATUS.Top;
      if(this.indexStart < 0) this.indexStart = 0;
      if(this.indexStart == 0 ) return;
      data = this.data.slice(0,this.indexStart);
      this.indexStart = 0;
    }else{

      data = this.data.slice(startIndex,this.indexStart);
      this.indexStart = startIndex;
    }

    this.result.unshift(...data);

    if(this.result.length > this.resultSize){
      const delArr =  this.result.splice(this.resultSize);
      if(delArr.length){
        this.indexEnd -= delArr.length;
        console.log('--[indexEnd]: ',delArr.length, this.indexStart, this.indexEnd);
      }
      const { scrollHeight } =  this.ele;
      setTimeout(() => {
        const size = scrollHeight/this.resultSize;
        
        this.ele.scrollTop = parseInt(size * (this.config.total) )
      }, 0);

    }

    return {
      status:this.status,
      data:this.result
    }
  }
}

export default new DataLoader();

3. elSelectMassData.vue,组件

<template>
<div>
  <h2>el-select大量数据优化</h2>
  <el-select
    v-model="value"
    placeholder="请选择">
    <el-option
      v-for="item in list"
      :key="item.value"
      :label="item.text"
      :value="item.value">
    </el-option>
  </el-select>

</div>
</template>

<script>
import banks from '@/data/banks.js'; //数据源
import dataLoader  from '@/utils/dataLoader'; //加载器

export default {
  cname:"ElSelect大量数据优化",
  data(){
    return{
    list: [],
    value: ''
  }},
  mounted(){
    const ele = document.querySelector(".el-select-dropdown__wrap.el-scrollbar__wrap");
    if(ele){
      dataLoader.init(banks,{
        ele, 
        total:10,
        resultSize:30,
        callback:this.getData
      });
    }
    
    const {data} =  dataLoader.getNext();
    this.list = data;
  },
  methods:{
    getData(res){
      this.list = {...res.data};
    },
  },
  
}
</script>

the End。