vue3 el-select懒加载以及自定义指令方式

 更新时间:2024年04月25日 09:25:50   作者:偷只猫来养  
这篇文章主要介绍了vue3 el-select懒加载以及自定义指令方式,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教

vue3 el-select懒加载以及自定义指令

要求:自定义搜索、自定义指令,滚动懒加载每次展示10条数据

在element-plus中,el-select的选项框是使用popper.js生成的,不在body中,无法直接获取,这时需要巧妙地使用popper-class属性来获取到dom并挂载到el-select进行监听来实现我们的需求。

下面是代码:

<template>
    <el-select 
        v-model="value"
        :loading="loading"
        loading-text="数据加载中..." 
        filterable
        clearable
        popper-class="event-select-poper"
        v-el-select-loadmore="loadmore"
        :placeholder="placeholder"
        style="width: 100%"
        :filter-method="filterOptions"
        @change="handleEventsChange"
        @visible-change="handleVisibleChange">
        <el-option
            v-for="(item, index) in options"
            :key="index"
            :label="item.eventTheme"
            :value="item.logCode">
        </el-option>
    </el-select>
</template>

在vue3的setup语法糖中,以v开头的变量会被识别为指令,一般指令变量命名使用小驼峰语法

<script setup>
import { ref, reactive, watch, nextTick, onBeforeMount } from 'vue'

const props = defineProps({
    eventId: {
        type: [String, Number],
        default: ''
    },
    placeholder: {
        type: String,
        default: ''
    }
})

const emits = defineEmits(['change'])

let options = reactive([])
let loading = ref(false)
let value = ref('')
let allEvents = reactive([
    { eventTheme: '1', logCode: 1 },
    { eventTheme: '2', logCode: 2 },
    { eventTheme: '3', logCode: 3 },
    { eventTheme: '4', logCode: 4 },
    { eventTheme: '5', logCode: 5 },
    { eventTheme: '6', logCode: 6 },
    { eventTheme: '7', logCode: 7 },
    { eventTheme: '8', logCode: 8 },
    { eventTheme: '9', logCode: 9 },
    { eventTheme: '10', logCode: 10 },
    { eventTheme: '11', logCode: 11 },
    { eventTheme: '12', logCode: 12 },
    { eventTheme: '13', logCode: 13 },
    { eventTheme: '14', logCode: 14 },
    { eventTheme: '15', logCode: 15 },
    { eventTheme: '16', logCode: 16 },
])
let allFilterEvents = reactive([
{ eventTheme: '1', logCode: 1 },
    { eventTheme: '2', logCode: 2 },
    { eventTheme: '3', logCode: 3 },
    { eventTheme: '4', logCode: 4 },
    { eventTheme: '5', logCode: 5 },
    { eventTheme: '6', logCode: 6 },
    { eventTheme: '7', logCode: 7 },
    { eventTheme: '8', logCode: 8 },
    { eventTheme: '9', logCode: 9 },
    { eventTheme: '10', logCode: 10 },
    { eventTheme: '11', logCode: 11 },
    { eventTheme: '12', logCode: 12 },
    { eventTheme: '13', logCode: 13 },
    { eventTheme: '14', logCode: 14 },
    { eventTheme: '15', logCode: 15 },
    { eventTheme: '16', logCode: 16 },
])
let pageNum = ref(1)
let pageSize = ref(10)

// 自定义v-el-select-loadmore指令
const vElSelectLoadmore = {
    beforeMount(el, binding) {
        /** 
         * vue2时:
         * el.querySelector('.el-select-dropdown .el-select-dropdown__wrap');
         * vue3时:
         * 在el-select给一个参数popper-class="event-select-poper"           
         * element-plus中el-select的选项是使用的popper.js生成的,无法直接获取
        */
		const selectDom = document.querySelector('.event-select-poper .el-select-dropdown__wrap')
		let loadMores = function() {
            /**
            * scrollHeight 获取元素内容高度(只读)
            * scrollTop 获取或者设置元素的偏移值,常用于, 计算滚动条的位置, 当一个元素的容器没有产生垂直方向的滚动条, 那它的scrollTop的值默认为0.
            * clientHeight 读取元素的可见高度(只读)
            * 如果元素滚动到底, 下面等式返回true, 没有则返回false:
            * ele.scrollHeight - ele.scrollTop === ele.clientHeight;
            */
            // 判断是否到底
			const isBase = this.scrollHeight - this.scrollTop <= this.clientHeight + 20
			if (isBase) {
                // 增加防抖
				binding.value && binding.value()
			}
		}
        // 将获取到的dom和函数挂载到el-select上,实例销毁时好处理
		el.selectDomInfo = selectDom
		el.selectLoadMore = loadMores
        // 监听滚动事件
		selectDom?.addEventListener('scroll', loadMores.bind(selectDom))
	},
    // 实例销毁
	beforeUnmount(el) {
		if (el.selectLoadMore) {
			el.selectDomInfo.removeEventListener('scroll', el.selectLoadMore)
			delete el.selectDomInfo
			delete el.selectLoadMore
		}
	}
}

watch(() => allFilterEvents, () => {
    let startIndex = pageNum.value * pageSize.value - pageSize.value
    let endIndex = pageNum.value * pageSize.value
    options = allFilterEvents.slice(startIndex, endIndex)
}, {
    immediate: true,
    deep: true
})

onBeforeMount(() => {
    loadEvents()
})

// 模拟懒加载
const loadmore = () => {
    // 数据加载完成之后,不需要再执行懒加载
    if (allEvents.length <= options.length) { return }
    pageNum.value++
    nextTick(() => {
        loading.value = true
        let startIndex = pageNum.value * pageSize.value - pageSize.value
        let endIndex = pageNum.value * pageSize.value
        options = [
            ...options,
            ...allFilterEvents.slice(startIndex, endIndex)
        ]
        loading.value = false
    })
}

// 自定义过滤函数
const filterOptions = (query = '') => {
    pageNum.value = 1
    nextTick(() => {
        if(query === ''){
            // 搜索词为空时,显示全部
            allFilterEvents = JSON.parse(JSON.stringify(allEvents))
        }else{
            // 搜索词不为空时,从所有关键字列表中筛选匹配项
            let arr = allEvents.filter((item) => {
                return item.includes(query)
            })
            $set(this, 'allFilterEvents', arr)
        }
    })
}

// 下拉框隐藏时,重置分页已经过滤得到的列表
const handleVisibleChange = (visible) => {
    if(!visible){
        pageNum.value = 1
        nextTick(() => {
            allFilterEvents = JSON.parse(JSON.stringify(allEvents))
        })
    }
}

// 获取事件列表
const loadEvents = () => {
    loading.value = true
    allFilterEvents = allEvents
    // 调用接口
    loading.value = false
}

const handleEventsChange = (val) => {
    emits('change', val)
}
</script>

el-select懒加载(自定义指令selectLazyload)实现大数据量展示和搜索

需求:在一个el-form里使用el-select提供选择用户的下拉框,但是每次打开弹框的时候由于数据量过大都需要加载很久才能将数据渲染出来,并且页面异常卡顿

封装懒加载指令

在指令文件夹下新建文件 selectLazyload.js

export default {
    bind (el, binding) {
        // 获取选择器下的滚动框DOM
        const SELECTWRAP_DOM = el.querySelector(
            `.el-select-dropdown__wrap`
        ); 
        // 如果找到了滚动框DOM,则添加滚动事件监听器
        if (SELECTWRAP_DOM) {
            SELECTWRAP_DOM.addEventListener("scroll", function () {
                // 如果滚动到底部,则调用传入的回调函数
                const condition = SELECTWRAP_DOM.scrollHeight - SELECTWRAP_DOM.scrollTop <= SELECTWRAP_DOM.clientHeight;
                if (condition) {
                    binding.value();
                }
            });
        }
    },
};

在main.js或directive/index.js中注册这个自定义指令

import selectLazyLoad from './module/selectLazyLoad'
Vue.directive('selectLazyLoad', selectLazyLoad)

html结构:在el-select上添加 

v-selectLazyLoad="loadMore":filter-method="userFilterMethod"

<el-select v-model="transferTesterNo" filterable placeholder="请选择测试人" style="width:100%" v-selectLazyLoad="loadMore" :filter-method="userFilterMethod">
	<UOptins :isMobile="isMobile" v-for="{nickName, userName, organName, roleName} of options" :key="userName" :label="nickName" :organName="organName" :value="userName" :roleName="roleName" />
</el-select>

js结构

// 加载更多
loadMore(){
	// 如果截取的长度==获取到的所有option长度将不再截取
	if (this.options.length == this.userOptions?.length) return;
	this.userPageNum++;
	this.getScrollUser();
},
 
// 截取页面显示的实际option
getScrollUser(){
	// 过滤出前5条用户信息  实际展示的数据 options  全部数据 userOptions
	this.options = this.userOptions.slice(0, this.userPageNum * 5);
},
// 搜索功能
userFilterMethod(value){
	// 手动触发下拉框回滚至顶部,避免触发v-selectLazyLoad指令
	document.querySelector(`.el-select-dropdown__wrap`).scrollTop = 0;
	if (!value?.length) return this.getScrollUser();
	this.options = this.userOptions.filter((item) => item.nickName?.includes(value)) || [];
},

首次加载需要在 获取数据的接口内写上

this.userOptions = rows
this.getScrollUser()

// 在data 中定义变量 
userOptions: [],
options: [],
userPageNum: 1,

这样即可实现 在滚动到底部时动态加载后面的数据,且可以搜索

总结

以上为个人经验,希望能给大家一个参考,也希望大家多多支持脚本之家。

相关文章

  • Vue项目设置多个静态文件及自定义静态文件目录的方案详解

    Vue项目设置多个静态文件及自定义静态文件目录的方案详解

    本文介绍了如何在Vue项目中配置多个静态文件目录,并提供了使用Vite和Webpack实现的示例,通过在vite.config.ts或vue.config.js中引入相关插件和配置,可以轻松实现自定义静态文件目录,希望这些内容对您有所帮助,感兴趣的朋友一起看看吧
    2025-01-01
  • vue函数防抖与节流的正确使用方法

    vue函数防抖与节流的正确使用方法

    防抖和节流的作用都是防止函数多次调用,下面这篇文章主要给大家介绍了关于vue函数防抖与节流的正确使用方法,需要的朋友可以参考下
    2021-05-05
  • 在项目中封装axios的实战过程

    在项目中封装axios的实战过程

    这篇文章主要给大家介绍了关于如何在项目中封装axios的相关资料,axios 请求的封装,无非是为了方便代码管理,文中通过示例代码介绍的非常详细,需要的朋友可以参考下
    2021-09-09
  • 在vue项目中,将juery设置为全局变量的方法

    在vue项目中,将juery设置为全局变量的方法

    今天小编就为大家分享一篇在vue项目中,将juery设置为全局变量的方法,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧
    2018-09-09
  • 在vue中nextTick用法及nextTick 的原理是什么

    在vue中nextTick用法及nextTick 的原理是什么

    这篇文章主要介绍了在vue中nextTick用法及nextTick 的原理是什么,Vue.js 是一个流行的前端框架,它提供了一种响应式的数据绑定机制,使得页面的数据与页面的 UI 组件之间能够自动同步,需要的朋友可以参考下
    2023-04-04
  • vue打印功能实现的两种方法总结

    vue打印功能实现的两种方法总结

    在项目中,有时需要打印页面的表格,所以下面这篇文章主要给大家介绍了关于vue打印功能实现的两种方法,以及批量打印的完整代码,需要的朋友可以参考下
    2021-06-06
  • 一个因@click.stop引发的bug的解决

    一个因@click.stop引发的bug的解决

    这篇文章主要介绍了一个因@click.stop引发的bug的解决,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2019-01-01
  • vue中的h函数使用及说明

    vue中的h函数使用及说明

    这篇文章主要介绍了vue中的h函数使用及说明,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教
    2024-04-04
  • Vue中挂载全局的方法详解

    Vue中挂载全局的方法详解

    有时候,频繁调用的函数,我们需要把它挂载在全局的vue原型上,方便调用,这篇文章主要为大家详细介绍了Vue中挂载全局的具体操作,需要的可以参考下
    2024-03-03
  • Vue单页面应用做预渲染的方法实例

    Vue单页面应用做预渲染的方法实例

    vue是一个单页面的应用,这导致一些爬虫和百度无法搜索到,如果你想针对你应用的其中某些页面进行SEO优化,让他们可以被爬虫和百度搜索到,你可以进行预渲染操作,下面这篇文章主要给大家介绍了关于Vue单页面应用做预渲染的相关资料,需要的朋友可以参考下
    2021-10-10

最新评论