前言

本文采主要用了自定义指令结合绑定滚动条事件的方式

问题描述

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

需求

  1. 默认展示20条
  2. 滚动到底部再加载20条或剩余数据
  3. 同时支持搜索,(符合搜索条件的数据也要支持滚动加载)
  4. 无论是滚动加载还是搜索出的数据只加载一次,不做重复加载
  5. 搜索需要做防抖处理,来尽量避免不必要的搜索操作
一、自定义指令完成滚动加载的功能

1.第一步:在自定义指令中绑定滚动事件触发执行加载数据方法

directives: {
    "el-select-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-el-select-loadmmore="loadmore"
               :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'
	},...
]

至此element的懒加载联合搜索就结束了

问题

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

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

 <ul class="infinite-list" v-infinite-scroll="load" style="overflow:auto">
    <li v-for="i in count" class="infinite-list-item">{{ i }}</li>
  </ul>

这里的ul是一个内部有滚动条可滚动的元素,但是我们如果想给el-select使用的话会发生什么事情,废话不多说,上示例
1.首先给el-select一个类名

在这里插入图片描述
2.我们来看渲染的dom节点
在这里插入图片描述
但是值得注意的是,像emement-ui都会在组件内部有插槽的设置,这里利用默认插槽也是可以解决这个问题的

二、防抖与节流

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

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

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

归根结底两者都是在对控制触发频率这块做文章,只要理解这个思想,就不难实现。

首先给大家看一段简单的防抖代码:

其实事件每触发一次,用于防抖处理的方法也是都要执行一次的,只不过是将逻辑代码封装进了handle函数中,然后控制handle函数的触发频率。那么由此看来,如果能做到控制逻辑代码块的执行频率是不是也算是变相的防抖处理?
1.滚动事件的处理
在这里插入图片描述
2.搜索事件的处理
搜索事件因为无法利用代码限制判断出应该在哪一瞬间执行事件函数,所以就需要做防抖处理,做个2秒的防抖,这样如果用户是一个字一个字的输入,就不会频繁的在每一次输入字符都要执行一次函数,若超过2秒,则认为用户输入完毕,执行事件函数

结束语

如有理解的不对的地方,还望多多包涵,本意只在记录问题。

Logo

前往低代码交流专区

更多推荐