antd——封装select下拉框组件——解决数据量巨大时下拉框渲染缓慢的问题——基础积累

最近在写后台管理系统,遇到下拉框组件的使用需求。由于框架是antd的,因此需要用到的是
在这里插入图片描述
但是这个组件跟elementUi中的下拉选择器一样,当下拉框中的数据很多时,就会非常的卡顿。

之前我有写过一个关于elementUi中下拉组件数据很多卡顿的文章,最终是自己写了一个组件实现了想要的功能。

elementUi——select组件渲染数据很多时卡顿问题解决:链接

这次仿照之前的案例,对antdselect组件进行自己封装。

1.props父子组件传值

props: {
    value: {
      type: Object,
      default: () => {},
    },
    width: {
      type: String,
      default: "100",
    },
    list: {
      type: Array,
      default: () => [],
    },
    placeholer: {
      type: String,
      default: "请选择品牌",
    },
  },

由于props传值是异步的,因此当传递的数据是通过接口异步获取时,就会出现在组件的mounted周期函数中拿不到父组件传递过来的数据的情况,此时可以通过watch监听来实现。

watch: {
  value: {
    handler(newName, oldName) {
      this.selectValue = { ...newName };
    },
    immediate: true,
    deep: true,
  },
  list: {
    handler(newName, oldName) {
      this.arr = [...newName];
    },
    immediate: true,
    deep: true,
  },
},

定义的变量:

data() {
    return {
      showData: false,
      selectValue: {},
      searchTimer: null,
      arr: [],
      mouseFlag: false,
      top: 0,
      left: 0,
      offsetLeft: 0,
    };
  },

2.组件组成:input搜索框+div下拉选项框

在这里插入图片描述

2.1 input搜索框

 <a-input
      type="text"
      v-model="selectValue.name"
      :placeholder="placeholer"
      @focus="inputFocus"
      @blur="inputBlur"
      @change="inputChange"
      allowClear
      :style="{ width: width + 'px' }"
    ></a-input>

关于这个组件,需要实现以下功能:

  1. focus:光标移入时:输入框内容要清空,div的下拉选项框要显示。
  2. blur:光标移出时:输入的内容要清空,展示已选择的内容,div的下拉框要隐藏
  3. change:监听输入框的内容,实时进行下拉框内容的筛选
2.1.1 focus代码
inputFocus(e) {
      var wh = document.documentElement.clientHeight,
        ot = e.target.getBoundingClientRect().top;
      if (
          (Math.abs(wh - ot) > 300 && Math.abs(wh - ot) < 360) ||
          Math.abs(wh - ot) < 250
        ) {
          this.top = e.target.offsetTop - 150;
        } else {
          this.top = e.target.offsetTop + 40;
        }
      console.log("left", e.target.offsetLeft);
      this.offsetLeft = e.target.offsetLeft;
      this.left = e.target.offsetLeft;
      this.selectValue = {};
      if (this.arr && this.arr.length) {
        this.showData = true;
      } else {
        this.$message.error("无相关数据");
      }
    },
2.1.2 blur代码
inputBlur(e) {
  setTimeout(() => {
    if (!this.mouseFlag) {
      this.showData = false;
    }
    this.selectValue = this.value;
    this.arr = [...this.list];
  }, 200);
},
2.1.3 change代码
//监听输入
inputChange(e) {
  var val = e.target.value;
  if (val) {
    clearTimeout(this.searchTimer);
    this.searchTimer = setTimeout(() => {
      this.arr = this.list.filter((l) => {
        return l.name.indexOf(val.toLowerCase()) > -1;
      });
    }, 50);
  } else {
    this.arr = [...this.list];
  }
  if (!val) {
    this.$emit("ok", {});
  }
},

2.2 div下拉选项框

<div
  class="selectOptionCls"
  :style="{ top: top + 'px', left: left + 'px', width: width + 'px' }"
  v-show="showData"
  ref="selectOptionCls"
  @mouseenter="mouseenter"
  @mouseleave="mouseleave">
  <div
    v-for="data in arr"
    :key="data.id"
    :class="data.id == selectValue.id ? 'active' : ''"
    @click="selectVal(data)"
  >
    {{ data.name }}
  </div>
</div>

关于这个鼠标移入和移出的问题,当光标在input组件中时,下拉选择框显示,当鼠标移入到下拉选择框中时,下拉选择框还需要显示,而非隐藏,因此需要监听下拉选择框中是否有鼠标移入和移出的操作。

相应的代码如下:

mouseleave() {
  this.mouseFlag = false;
},
mouseenter() {
  this.mouseFlag = true;
},
//选择某一项
selectVal(data) {
  this.$emit("ok", data);//选择后将选中的数据抛出
  this.mouseFlag = false;
  this.showData = false;
},

3.style样式代码

<style scoped lang="less">
.wrap {
  height: 40px;
  z-index: 1;
  // -webkit-transition: all 0.2s;
  // transition: all 0.2s;
}
.selectOptionCls {
  background: #fff;
  height: 150px;
  position: absolute;
  top: 40px;
  left: 0;
  z-index: 9;
  border-radius: 4px;
  color: #999;
  border: 1px solid #d9d9d9;
  box-sizing: border-box;
  overflow: auto;
  div {
    text-overflow: -o-ellipsis-lastline;
    overflow: hidden;
    text-overflow: ellipsis;
    display: -webkit-box;
    -webkit-line-clamp: 1;
    line-clamp: 1;
    font-size: 14px;
    -webkit-box-orient: vertical;
    padding: 0 10px;
    line-height: 30px;
    &.active {
      font-weight: bold;
    }
    &:hover {
      cursor: pointer;
      background: #fff9e6;
    }
  }
}
</style>

父组件中的使用

<big-data-select
  :style="`width:${
   width - 10
  }px;text-align:left;left:-${scrollLeft}px;top:-10px;`"
  :value="{ id: record.mfgId, name: record.mfg }"
  @ok="selectVal($event, record)"
  :width="`${col.width - 10}`"
  :list="brandList"
  placeholer="请选择品牌"
/>

注意上面的数据:

对应props中的:width:宽度 list:下拉框的数据 placeholer:输入框中的提示信息 value:展示的值,是个对象,属性有mfgIdmfg两个。

选择某一项后触发的函数:

selectVal(data, row) {
 if (data && data.id) {
    row.mfgId = data.id;
    var arr = this.brandList.filter((brand) => brand.id == data.id);
    if (arr && arr[0]) {
      row.mfg = arr[0].name;
    }
  } else {
    row.mfgId = undefined;
    row.mfg = undefined;
  }
},

此时发现功能已经实现了。
但是出现了层级问题,就是下拉框中的内容会被遮挡。此时可以给子组件外面添加一个绝对定位的div标签来实现层级问题。

<div style="position: absolute">
 子组件
</div>

这样就可以了。

但是又出现了新的问题。由于现在给子组件进行的是绝对定位,则滚动条滚动时,下拉框的部分并不会跟随滚动条滚动,绝对定位的元素是跟随页面位置的。
在这里插入图片描述
这个时候我想到的解决办法就是:当页面滚动时,不展示子组件,只有当滚动结束时再展示子组件。

<div v-if="!scrollFlag" style="position: absolute">
	子组件
</div>
<div v-else>子组件的值xxxx</div>

此时的问题变成了,在vue代码中监听表格滚动事件。

vue代码中监听表格滚动事件

mounted() {
    this.$nextTick(() => {
      var bodyDom =
        this.$refs.table.$el &&
        this.$refs.table.$el.getElementsByClassName("ant-table-body")[0];
      bodyDom.addEventListener(
        "scroll",
        (e) => {
          clearTimeout(this.timer);
          this.timer = null;
          this.scrollLeft = e.target.scrollLeft;
          this.scrollFlag = true;
          this.timer = setTimeout(() => {
            if (this.scrollLeft == e.target.scrollLeft) {
              //停止滑动
              this.scrollFlag = false;
            }
          }, 200);
        },
        false
      );
    });
  },

监听滚动事件:dom.addEventListener('scroll',函数,false)

如果延迟200毫秒时滚动的距离与上一次滚动距离相等,则视为滚动结束。也就是上面的this.timer = setTimeout的部分。

最终效果如下:
在这里插入图片描述
在这里插入图片描述

完成!!!

  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

叶浩成520

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值