尧图网络 高端网站定制 · 原创设计
免费咨询热线
400-888-6620
免费获取方案
Vue3 + ElementPlus 2.4.x 自定义指令:解决el-select滚动加载3大坑点
Vue3 ElementPlus 2.4.x 自定义指令解决el-select滚动加载3大坑点在Vue3项目中ElementPlus的el-select组件因其丰富的功能和良好的兼容性成为开发者处理下拉选择需求的首选。然而当面对大数据量场景时直接一次性渲染所有选项会导致严重的性能问题。滚动加载Infinite Scroll是解决这一问题的有效方案但在实际开发中我们往往会遇到几个棘手的坑点。1. Teleport机制带来的DOM结构变化ElementPlus 2.4.x版本中el-select的下拉菜单默认通过Teleport挂载到body元素下。这一变化导致传统的DOM查询方式失效我们需要调整自定义指令的实现逻辑。export default { beforeMount(el, binding) { // 使用popper-class作为查询选择器 const selectWrap document.querySelector( .${binding.arg} .el-select-dropdown__wrap ) const scrollHandler function() { const isBottom this.scrollHeight - this.scrollTop this.clientHeight 5 if (isBottom) { binding.value() } } selectWrap?.addEventListener(scroll, scrollHandler) el._scrollHandler scrollHandler el._selectWrap selectWrap }, beforeUnmount(el) { el._selectWrap?.removeEventListener(scroll, el._scrollHandler) } }关键调整点不再直接查询.el-select-dropdown__wrap而是通过传入的popper-class定位添加5px的容差阈值避免精确匹配导致的触发不灵敏将DOM引用和事件处理器挂载到el上便于卸载时清理提示建议为每个el-select设置唯一的popper-class避免多个下拉框互相干扰。2. 事件监听与内存泄漏防范滚动加载的核心是监听滚动事件但如果处理不当很容易造成内存泄漏。以下是常见的三种问题场景及解决方案2.1 事件未正确移除// 错误示例直接添加匿名函数无法移除 selectWrap.addEventListener(scroll, function() { // ... }) // 正确做法使用具名函数引用 const handler function() { /*...*/ } selectWrap.addEventListener(scroll, handler) // 卸载时 selectWrap.removeEventListener(scroll, handler)2.2 组件卸载时未清理{ beforeUnmount(el) { // 必须检查元素是否存在 if (el._selectWrap el._scrollHandler) { el._selectWrap.removeEventListener(scroll, el._scrollHandler) } // 清除引用 delete el._selectWrap delete el._scrollHandler } }2.3 动态popper-class处理当popper-class是动态生成时需要特殊处理template el-select :popper-classselect-${uniqueId} v-select-loadmore:[select-${uniqueId}]loadMore !-- options -- /el-select /template script setup import { nanoid } from nanoid const uniqueId nanoid() /script3. 性能优化与用户体验提升单纯的滚动加载实现可能还不够我们需要考虑以下优化点3.1 防抖处理import { debounce } from lodash-es const scrollHandler debounce(function() { const isBottom this.scrollHeight - this.scrollTop this.clientHeight 5 if (isBottom !loading.value) { binding.value() } }, 200, { leading: true, trailing: true })参数说明参数类型默认值说明leadingbooleanfalse是否在延迟开始前调用trailingbooleantrue是否在延迟结束后调用maxWaitnumber-最大等待时间3.2 加载状态反馈template el-select v-select-loadmoreloadMore el-option v-foritem in options :valueitem.value :labelitem.label / template #footer div v-ifloading classloading-more el-icon classis-loadingLoading //el-icon 加载中... /div div v-else-ifnoMore classno-more没有更多了/div /template /el-select /template style .loading-more, .no-more { padding: 8px 0; text-align: center; color: var(--el-text-color-secondary); font-size: 12px; } /style3.3 数据分页策略推荐的后端接口参数设计const loadMore async () { if (loading.value || noMore.value) return loading.value true try { const res await api.getList({ page: page.value 1, pageSize: 20, keyword: searchKeyword.value }) if (res.data.length) { options.value.push(...res.data) page.value } else { noMore.value true } } finally { loading.value false } }分页参数对比分页方式优点缺点适用场景页码分页实现简单数据新增时可能重复数据变动不频繁游标分页避免重复实现复杂实时数据流时间分页自然排序依赖时间字段时间序列数据4. 完整实现与最佳实践下面是一个生产环境可用的完整实现方案4.1 自定义指令封装// directives/selectLoadmore.js import { debounce } from lodash-es export default { beforeMount(el, binding) { const { arg: popperClass, value: callback } binding if (typeof callback ! function) { console.warn([v-select-loadmore] 必须传入函数) return } const selectWrap document.querySelector( .${popperClass} .el-select-dropdown__wrap ) if (!selectWrap) { console.warn([v-select-loadmore] 未找到下拉框容器请检查popper-class: ${popperClass}) return } const checkBottom function() { const { scrollHeight, scrollTop, clientHeight } this return scrollHeight - scrollTop clientHeight 5 } const scrollHandler debounce(function() { if (checkBottom.call(this)) { callback() } }, 200) selectWrap.addEventListener(scroll, scrollHandler) // 存储引用以便清理 el._selectLoadmore { selectWrap, scrollHandler } }, beforeUnmount(el) { const { selectWrap, scrollHandler } el._selectLoadmore || {} if (selectWrap scrollHandler) { selectWrap.removeEventListener(scroll, scrollHandler) } delete el._selectLoadmore } }4.2 全局注册// main.js import { createApp } from vue import App from ./App.vue import selectLoadmore from ./directives/selectLoadmore const app createApp(App) app.directive(select-loadmore, selectLoadmore) app.mount(#app)4.3 组件使用示例template el-select v-modelselectedValue filterable remote :remote-methodhandleSearch :popper-classselectPopperClass v-select-loadmore:[selectPopperClass]loadMore visible-changehandleVisibleChange el-option v-foritem in options :keyitem.value :labelitem.label :valueitem.value / template #footer div v-ifloading classselect-footer el-icon classis-loadingLoading //el-icon span加载中.../span /div div v-else-ifisEnd classselect-footer没有更多数据了/div /template /el-select /template script setup import { ref, onMounted } from vue import { nanoid } from nanoid const selectPopperClass select-${nanoid()} const selectedValue ref() const options ref([]) const loading ref(false) const isEnd ref(false) const pageInfo ref({ page: 1, pageSize: 20, keyword: }) const fetchData async (reset false) { if (loading.value) return loading.value true try { const res await api.getData({ ...pageInfo.value, page: reset ? 1 : pageInfo.value.page }) if (reset) { options.value res.data isEnd.value false } else { options.value.push(...res.data) } if (res.data.length pageInfo.value.pageSize) { isEnd.value true } else if (!reset) { pageInfo.value.page } } finally { loading.value false } } const loadMore () { if (!isEnd.value !loading.value) { fetchData() } } const handleSearch (keyword) { pageInfo.value.keyword keyword fetchData(true) } const handleVisibleChange (visible) { if (!visible) { // 重置状态 pageInfo.value.page 1 isEnd.value false } } onMounted(() { fetchData(true) }) /script style .select-footer { padding: 8px 16px; text-align: center; color: var(--el-text-color-secondary); font-size: 12px; display: flex; align-items: center; justify-content: center; gap: 6px; } /style在实际项目中我们还需要考虑以下细节网络错误时的重试机制空数据状态展示移动端触摸事件优化多选模式下的特殊处理
RELATED

相关推荐

下载大文件总被限速?这款全能下载软件,BT磁力链全通吃,10任务+16线程快到飞起!

下载大文件总被限速?这款全能下载软件,BT磁力链全通吃,10任务+16线程快到飞起!

下载资源时,浏览器自带下载器慢如蜗牛,BT种子找不到靠谱工具,磁力链接更是直接"无法识别"?更崩溃的是,好不容易找到个下载软件,界面复杂得像开飞机,任务一多就卡顿崩溃,单…

📅 2026/9/10 10:59:45
企业微信外部群机器人自动化:基于RPA的高效管理方案与实现思路

企业微信外部群机器人自动化:基于RPA的高效管理方案与实现思路

在企业微信的生态中,社群运营(尤其是外部群)的自动化管理一直是研发和运营关注的重点。官方提供的标准群机器人虽然安全稳定,但在外部群(包含组织外部成员的群聊)中存在较多的功能限制,例如无法…

📅 2026/9/10 11:25:01
3步配置GitHub智能DNS加速,让代码下载速度提升300%

3步配置GitHub智能DNS加速,让代码下载速度提升300%

3步配置GitHub智能DNS加速,让代码下载速度提升300% 【免费下载链接】FastGithub github定制版的dns服务,解析访问github最快的ip 项目地址: https://gitcode.com/gh_mirrors/fa/FastGithub FastGithub是一款专为GitHub优化的智能DNS解析工具&…

📅 2026/9/10 11:23:15
MORE NEWS

更多资讯

📰

电工证可以每年退税吗 你真以为含金量是天上掉下来的

电工证可以每年退税吗 你真以为含金量是天上掉下来的 怕考不过白交培训费?别急,先搞清楚电工证到底值不值这个钱。很多人误以为电工证是万能钥匙,能开任何门,但事实是, 电工证的含金量…

📰

工地太忙根本没时间复习考试?宾阳电工证考试网上查询攻略来了

工地太忙根本没时间复习考试?宾阳电工证考试网上查询攻略来了 工地太忙根本没时间复习考试,尤其是像宾阳这种工程项目集中的地方,电工证考试成了很多施工人员的“硬门槛”。但别急,现在通过【宾阳电工证考试网上查询】平台,加上一些实操经验,你照样能轻…

📰

电工证和教师待遇怎么比?证书过期了不知道怎么复审延期,官方报名入口帮你搞定

电工证和教师待遇怎么比?证书过期了不知道怎么复审延期,官方报名入口帮你搞定 证书过期了不知道怎么复审延期?别慌,现在就带你一步步搞清楚电工证和教师待遇之间的关系,还有怎么通过官方报名入口完成复审。特别提醒,像许昌的初学者,更要重视电工证的补…

📰

韶关电工证培训学校值不值得考?电工证是刚需还是鸡肋?

韶关电工证培训学校值不值得考?电工证是刚需还是鸡肋? 你现在可能正面临一个问题:急需特种作业证才能上岗找工作,但又对电工证值不值得考犹豫不决。特别是像你这样的劳务班组负责人,手里握着项目,却因为没有合适的电工证,被甲方或监理卡住进度,耽误工…

📰

办理宾阳电工证考试需要什么材料

电工证考试心里没底怕挂科?手把手教你拿下电子证书 实操考试心里没底怕挂科?很多人第一次参加宾阳电工证考试的时候,心里都打鼓,怕操作不规范、怕答错题、怕补办流程复杂,甚至担心拿不到电子证书。其实,只要掌握好流程和技巧,考试并不难。下面我就从…

📰

汽车维修电工证补贴值不值得考?郑州人避坑全攻略

汽车维修电工证补贴值不值得考?郑州人避坑全攻略 怕考不过白交培训费?这是很多郑州初次报考汽车维修电工证的小伙伴最怕听到的。尤其是现在国家对特种作业人员的管理越来越严格, 汽车维修电工证补贴…

TODAY

今日更新

THIS WEEK

本周精选

THIS MONTH

本月热门

读完文章,想聊聊您的网站?

告诉我们您的行业与需求,资深顾问一对一梳理方案与报价,全程免费。

📞 💬