vue.js

关注公众号 jb51net

关闭
首页 > 网络编程 > JavaScript > javascript类库 > vue.js > vue3 el-select懒加载以及自定义指令

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

作者:偷只猫来养

这篇文章主要介绍了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,

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

总结

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

您可能感兴趣的文章:
阅读全文