el-select下拉加载出现抖动问题

问题描述

现有一个页面,充斥着大量表单元素,首先要知道的是vue对于视图上的更新机制,在一个组件内若有元素发生变动,那么整个组件就会刷新渲染,所以将大量的表单元素放在一个组件内是会造成页面卡顿的现象。如果有下拉框一次性加载大量数据的情况,这个现象会格外的明显。虽然说将表单分割成多个组件会有帮助,但是效果不会很大,必须要解决下拉框一次性加载了大量元素这个根源问题。由于element自身没有对于下拉框(el-select)做懒加载的处理(也可以和element-ui的InfiniteScroll(无限滚动)联合处理),所以就需要我们自己手动实现。

需求

默认展示20条
滚动到底部再加载20条或剩余数据
同时支持搜索,(符合搜索条件的数据也要支持滚动加载)
无论是滚动加载还是搜索出的数据只加载一次,不做重复加载
搜索需要做防抖处理,来尽量避免不必要的搜索操作

一、自定义指令完成滚动加载的功能
1.第一步:在自定义指令中绑定滚动事件触发执行加载数据方法
directives: {
    "loadmmore": {
      bind (el, binding) {
        // 因为el-select最终会渲染成ul  li  ,所以我们要找到对应的ul元素节点,因为最底层的滚动条就是这个ul的
        const SELECTDOM = el.querySelector('.el-select-dropdown .el-select-dropdown__wrap')
        // 为对应的ul绑定滚动条滚动事件
        SELECTDOM.addEventListener('scroll', function () {
          const condition = this.scrollHeight - this.scrollTop == this.clientHeight
          // 判断滚动到底部
          if (condition) {
          // binding.value 为自定义指令绑定的值,因为绑定的是个方法,所以这里是方法调用,触发指令后执行加载数据
            binding.value()
          }
        })
 
      }
    }
  },

利用自定义指令就可以针对性的对使用了指令的el-select组件处理懒加载。

2.第二步:加载数据的方法
props: {
    seleteOptions: {
      type: Array,
      default: function () {
        return []
      }
    }
  },
data () {
    return {
      seVal: '',
      
      initOption:[], // 满足筛选条件的val集合
      filterVal: '', // 搜索条件
      filterArray:[],  // 所有可展示的数据 (符合搜索条件 && newData没有的数据)
      newData: [],  // 正在展示的数据或展示过的数据
    }
},
mounted () {
    this.filterArray = [...this.seleteOptions]
    // 初始时加载一次
    this.loadmore()
},
methods: {
    // 滚动加载
    loadmore () {
      const oldArray = this.filterArray  // 可展示预备数据
      const newArray = this.newData        // 展示的数据
      let pushArray = []                 // 新增的数据
 
      // 当加载完毕时 直接退出方法
      if (newArray.length === oldArray.length) return
 
      // 当剩余要加载的不足20个时  则加载全部的
      if (newArray.length + 20 > oldArray.length) {
        pushArray = oldArray.slice(0)
        this.filterArray.splice(0)
      } else {
        pushArray = oldArray.slice(0,20)
        this.filterArray.splice(0,20)
      }
      // 搜索后展示的数据
      this.newData = [...newArray, ...pushArray]
    },
}

loadmore方法每次执行都会从预备数据中拿取20条或者剩余数据

3.第三步:自定义搜索方法
filterMethod (params) {
      // 记录下搜索条件
      this.filterVal = params
 
      // 搜索方法
      let vals = []
      let filterArray = []
      this.seleteOptions.forEach((item, i) => {
        // 有搜索条件时  查找符合条件的
        if (params && item.label.includes(params)) {
          // 添加所有符合搜索条件的val值
          vals.push(item.value)
          // 寻找符合搜索条件且newData中没有出现过的数据
          if(!this.newData.find(fin=>fin.value === item.value)){
           filterArray.push(item)
          }
        }else if(!params){  // 无搜索条件时  获取所有剩余newData中没有出现过的数据
          if(!this.newData.find(fin=>fin.value === item.value)){
           filterArray.push(item)
          }
        }
      })
      
      this.initOption = [...vals]
      this.filterArray = [...filterArray]
 
      this.loadmore()
      
    }

因为要搜索和懒加载结合使用,所以就需要自定义搜索方法结合到懒加载的数据使用,做一些逻辑处理。

4.第四步 搜索的防抖处理
computed: {
    // 防抖
    filterMethodThrottle(){
      var time = null
      return (param)=>{
        if(time){
          clearTimeout(time)
        }
        time = setTimeout(()=>{
          // 搜索方法
            this.filterMethod(param)
            clearTimeout(time)
        },2000)
      }
    }
  }

组件使用

<el-select 
  v-model="seVal"
  filterable
  placeholder="请选择"
  v-loadmmore="loadmore"
  class="my_elemnt_select"
  :filter-method="filterMethodThrottle">
   <el-option 
      v-for="(item, i) in newData"
       :label="item.label"
       :value="item.value"
        v-show="!filterVal || initOption.includes(item.value)"
        :key="i + 'region2'">
      </el-option>
    </el-select>

测试数据

将测试数据传给props中的

[
	{
		label:'第一项',
		value:'1'
	},...
]

问题

一、使用v-infinite-scroll(无限滚动)

v-infinite-scroll是element的一个滚动加载数据的一个自定义指令,但是需要注意的是,这个指令是需要直接作用在可滚动的父元素上的,就像官方的例子

1.首先给el-select一个类名
在这里插入图片描述

2.我们来看渲染的dom节点

在这里插入图片描述

但是值得注意的是,像emement-ui都会在组件内部有插槽的设置,这里利用默认插槽也是可以解决这个问题的

二、防抖与节流

首先我们只需要了解防抖和节流主要是解决什么问题的,它们采取的手段虽然不同,

防抖可以简单理解为在频繁的操作中,我只触发间隔达到规定的那一次。

节流可以简单理解为不管你多频繁的操作,我就按照我规定的间隔去触发

其实事件每触发一次,用于防抖处理的方法也是都要执行一次的,只不过是将逻辑代码封装进了handle函数中,然后控制handle函数的触发频率。那么由此看来,如果能做到控制逻辑代码块的执行频率是不是也算是变相的防抖处理?

1.滚动事件的处理

  Vue.directive('loadmore', {
    bind(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() {
          const CONDITION = this.scrollHeight - this.scrollTop <= this.clientHeight + 10
          if (CONDITION) {
            if (timer) {
              clearTimeout(timer)
            }
            timer = setTimeout(() => {
              binding.value()
            }, 300)
          }
        })
      }
    }
  })

2.搜索事件的处理
搜索事件因为无法利用代码限制判断出应该在哪一瞬间执行事件函数,所以就需要做防抖处理,做个2秒的防抖,这样如果用户是一个字一个字的输入,就不会频繁的在每一次输入字符都要执行一次函数,若超过2秒,则认为用户输入完毕,执行事件函数

结束语

参考:项目中遇到这个问题,只做记录。滚动事件的处理 在项目中真实用到,可做参考。
el-select下拉框懒加载以及搜索联合处理+搜索防抖处理 buling---------------大乔的博客

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值