el-select远程查询大数据量分段加载

el-select远程查询大数据量分段加载

1.问题:

最近使用elementUI的el-select组件的远程查询功能,发现在大量数据(1w条以上)的时候出现页面卡死的情况,经过排查,产生问题的原因是前端渲染造成的。

2.解决思路

1.在查找解决方案的时候看到一个解决办法是加载前50条,效果是这样的:
在这里插入图片描述
2.上面那个方法已经解决了页面卡死的问题了,但是只显示了50条结果,其他的结果无法显示。顺着这个思路,我给最后一行(“只显示前50条…”)加了一个点击事件,鼠标点击的时候就多显示20条,效果是这样的:
在这里插入图片描述
在这里插入图片描述
3.再升级一下,可不可以不用点击?那就得加鼠标滚动事件,通过鼠标滚动加载更多数据,效果与第2点一样,只是不用点击就可以加载了。

3.代码

组件代码:

<!--
 * @Author: Wushiqi
 * @Descripttion: 远程搜索下拉框组件
 * @Date: 2020-08-26 18:17:42
 * @LastEditor: Wushiqi
 * @LastEditTime: 2020-09-02 12:33:45
-->
<template>
  <div>
    <el-form ref="remoteForm" :model="remoteForm" :rules="formRule">
      <el-form-item prop="result">
        <el-tooltip ref="toolTip" :content="remoteForm.result?remoteForm.result:placeholder" popper-class="remote-tooltip" placement="top" effect="light">
          <el-select
            v-if="hasRemote"
            id="remote-search"
            ref="remoteSearch"
            v-model="remoteForm.result"
            filterable
            remote
            clearable
            reserve-keyword
            :placeholder="placeholder"
            :size="size"
            class="remote-search"
            :remote-method="remoteMethod"
            :loading="loading"
            :title="result"
            @input.native="input"
            @change="change"
            @focus="focus"
            @mousewheel="mouseWheel"
          >
            <el-option
              v-for="item in numberList_"
              :key="item.value"
              :label="item.label"
              :value="item.value"
            />
            <div v-if="numberList.length > 9 && numberList_.length !== numberList.length" ref="moreDiv" class="search-keyword">
              <span @click="loadMore">加载更多...</span>
            </div>
            <div v-else class="search-keyword-bottom">
              <span>到底啦~</span>
            </div>
          </el-select>
        </el-tooltip>
      </el-form-item>
    </el-form>
  </div>
</template>

<script>
/**
 * 1.如何清除表单?
 *   使用v-if属性对组件进行创建和销毁。使用方法:传入:has-remote="hasRemote",
 *   hasRemote为true是创建组件,表单重置时使hasRemote=false;
 *   *如果是在对话框中,讲对话框的visible的值给has-remote即可
 */
export default {
  name: 'RemoteSearch',
  props: {
    numberList: { // 完整的查询结果
      default: () => [],
      type: Array
    },
    size: {
      default: 'mini',
      type: String
    },
    placeholder: {
      default: null,
      type: [Number, String]
    },
    hasRemote: {
      default: true,
      type: Boolean
    }
  },
  data() {
    return {
      loading: false,
      options: [],
      list: [],
      searchData: '',
      remoteForm: {
        result: ''
      },
      formRule: {},
      numberList_: [],
      i: 1
    }
  },
  computed: {
    result() {
      return this.remoteForm.result
    }
  },
  watch: {
    numberList() {
      this.numberList_ = this.numberList.slice(0, 10) // 先加载10条结果
      this.i = 1
    },
    result(newValue, oldValue) {
      this.searchData = newValue
    },
    hasRemote() {
      if (!this.hasRemote) {
        this.$refs.remoteForm.resetFields() // 组件销毁时重置输入框
        // window.removeEventListener('mousewheel', this.mouseWheel, false) // 销毁鼠标滚动事件
      }
    }
  },
  mounted() {
    console.log('组件挂载')
    window.addEventListener('mousewheel', this.mouseWheel)
  },
  destroyed() {
    window.removeEventListener('mousewheel', this.mouseWheel, false) // 组件销毁时销毁鼠标滚动事件
  },
  methods: {
    remoteMethod(query) {
      if (query !== '') {
        this.loading = true
        setTimeout(() => {
          this.loading = false
          this.options = this.numberList_.filter(item => {
            return item.label.toLowerCase()
              .indexOf(query.toLowerCase()) > -1
          }).slice(0, 10)
        }, 200)
      } else {
        this.options = []
      }
    },
    loadMore: function() {
      this.i++
      this.numberList_ = this.numberList.slice(0, 10 * this.i) // 多加载10条结果
    },
    input: function(searchData) {
      this.remoteForm.result = searchData.target.value
      this.$emit('input', searchData)
    },
    change: function(data) {
      this.$emit('change', data)
    },
    focus: function() {
      this.$emit('focus')
    },
    select: function(data) {
      this.$emit('select')
    },
    mouseWheel: function() {
      if (this.$refs.remoteSearch && this.$refs.remoteSearch.$refs && this.$refs.remoteSearch.$refs.scrollbar) {
        const scrollHeight = Math.round(this.$refs.remoteSearch.$refs.scrollbar.moveY) // 滚动条滚动的距离
        console.log(scrollHeight);
        console.log(36 * this.i - 1);
        if (scrollHeight && scrollHeight !== 0 && scrollHeight > 36 * this.i) {
          this.loadMore()
        }
      }
    }
  }
}
</script>
<style lang="scss">
.remote-search {
  width: 100%;
  .el-input__inner {
    padding: 2px 5px;
  }
}
.remote-search {
  input::-webkit-input-placeholder {
    color: #616161a1;
  }
  input::-moz-input-placeholder {
    color: #6161618c;
  }
  input::-ms-input-placeholder {
    color: #61616173;
  }
}
.remote-tooltip {
  padding: 5px!important;
}
.search-keyword {
  text-align: center;
  padding: 5px;
  color: rgb(133, 133, 133);
  font-size: 12px;
  cursor: pointer;
  :hover {
    color: rgb(71, 159, 218);
  }
}
.search-keyword-bottom {
  text-align: center;
  padding: 5px;
  color: rgb(133, 133, 133);
  font-size: 12px;
}
</style>

调用:

changeResultDlgVisible 是所在表单是否显示(值为false和true) startNumber 默认回显的数据
resultNumberInput()是访问后台接口,获取查询结果
numberList是接口返回的查询结果,格式如下,须有value和label两个字段
在这里插入图片描述

<remote-search
  :result="startNumber"
  :number-list="numberList"
  :placeholder="startNumber"
  :has-remote="changeResultDlgVisible"
  size="small"
  @input="resultNumberInput(startNumber, 'paperNumber', $event.target.value)"
  @change="startNumber=$event;$forceUpdate"
  @focus="numberList = []"
/>

就可以啦~

©️2020 CSDN 皮肤主题: 技术工厂 设计师:CSDN官方博客 返回首页