欢迎来到尧图网

客户服务 关于我们

您的位置:首页 > 科技 > 能源 > vue 工具函数 useInfiniteScroll实现数据懒加载

vue 工具函数 useInfiniteScroll实现数据懒加载

2025/6/23 12:06:04 来源:https://blog.csdn.net/qq_45327886/article/details/148638995  浏览:    关键词:vue 工具函数 useInfiniteScroll实现数据懒加载

一、问题

1. 大量数据在前端一次性渲染会出现 页面卡顿甚至失去响应,影响用户体验。

2. 为了解决这个问题:一般有两种思路

        思路一:只渲染少量数据——分页显示、下拉搜索更新数据

        思路二:渲染可视区域内的数据——滚动到可视区域底部再加载(懒加载)

二、useInfiniteScroll滚动懒加载

1.具体思路

·      1)一开始获取到所有的数据searchedControlSales

        2)渲染指定数量的数据 showOptions

        3)useInfiniteScroll滚动到 渲染区域的底部时,再追加要 渲染的数据

          注:需要判断什么时候不再继续追加数据,数据加载完成时,避免不必要的追加行为。(判断方法:已渲染的数据量===总数据量)

2.原始代码

<template><div><div class="h-8 flex items-center"><span class="text-[#4d4d4d]">当前已选:</span><span class="font-semibold text-[1a1a1a]">{{ currentControlSale }}</span></div><div class="check-group-container custom-scroll-bar"><AFormItemRest><AInputSearchstyle="width: 200px"placeholder="请输入搜索关键字"class="mb-4":disabled="disabled"@change="searchControl"/></AFormItemRest><ARadioGroup v-model:value="bindValue" :disabled="disabled" class="!block"><div class="radios"><div v-for="item of searchedControlSales" :key="item.storeGroupId" class="flex items-center"><ARadio :value="item.storeGroupId" style="margin-right: 0">{{ item.groupName }}</ARadio><ATooltip v-if="item.groupType === 5" :title="item.groupDesc"><SvgIcon name="右箭头-中" class="cursor-pointer" :size="8.5" /></ATooltip><SvgIconv-elsename="右箭头-中"class="cursor-pointer":size="8.5"@click="viewControl(item.storeGroupId)"/></div></div></ARadioGroup></div></div>
</template><script lang="ts" setup>
import { getRestrictGroups, getStoreGroupList } from '@/api/commodity-management/goodsOnShelves'
import { RestrictGroup } from '@/api/commodity-management/model/groupEditModel'
import { iframeModal } from '@/components/iframeModal'
import { useVModel } from '@vueuse/core'
import { cloneDeep } from 'lodash-es'
import { WholesaleType } from '@/enum/common'interface Props {value?: numberoptions?: RestrictGroup[]disabled?: booleanwholesaleType?: WholesaleType
}const props = defineProps<Props>()
const emit = defineEmits(['update:value'])
const bindValue = useVModel(props, 'value', emit)const controlSales = ref<RestrictGroup[]>([])
const searchedControlSales = ref<RestrictGroup[]>([])watch(() => props.options,() => {setControlSales()}
)setControlSales()
async function setControlSales() {if (props.options) {controlSales.value = props.options} else {try {controlSales.value = await getStoreGroupList({ wholesaleType: props.wholesaleType })} catch (error) {console.error(error)}}searchedControlSales.value = cloneDeep(controlSales.value)
}function viewControl(storeGroupId: number) {iframeModal({url: `/provider.php/Admin/SaleControl/groupStore?store_group_id=${storeGroupId}`,fullScreen: true,title: '查看控销药店'})
}function searchControl(e: Event) {if (e.target instanceof HTMLInputElement) {const keyWord = e.target.valueif (!keyWord) {searchedControlSales.value = cloneDeep(controlSales.value)}searchedControlSales.value = controlSales.value.filter((item) => item.groupName.includes(keyWord))}
}
const currentControlSale = computed(() => {return controlSales.value.find((item) => item.storeGroupId === bindValue.value)?.groupName || ''
})
</script><style lang="less" scoped>
.check-group-container {padding: 16px;margin-top: 5px;max-width: 593px;max-height: 168px;overflow-y: auto;background: #fafafa;border-radius: 4px;.radios {flex-wrap: wrap;display: flex;gap: 16px 20px;}
}
</style>

3.useInfiniteScroll 懒加载后的代码

<template><div><div class="h-8 flex items-center"><span class="text-[#4d4d4d]">当前已选:</span><span class="font-semibold text-[1a1a1a]">{{ currentControlSale }}</span></div><div ref="radioGroup" class="check-group-container custom-scroll-bar"><AFormItemRest><AInputSearchstyle="width: 200px"placeholder="请输入搜索关键字"class="mb-4":disabled="disabled"@change="searchControl"/></AFormItemRest><ARadioGroup v-model:value="bindValue" :disabled="disabled" class="!block"><div class="radios"><div v-for="item of showOptions" :key="item.storeGroupId" class="flex items-center"><ARadio :value="item.storeGroupId" style="margin-right: 0">{{ item.groupName }}</ARadio><ATooltip v-if="item.groupType === 5" :title="item.groupDesc"><SvgIcon name="右箭头-中" class="cursor-pointer" :size="8.5" /></ATooltip><SvgIconv-elsename="右箭头-中"class="cursor-pointer":size="8.5"@click="viewControl(item.storeGroupId)"/></div></div></ARadioGroup></div></div>
</template><script lang="ts" setup>
import { getStoreGroupList } from '@/api/commodity-management/goodsOnShelves'
import type { RestrictGroup } from '@/api/commodity-management/model/groupEditModel'
import { iframeModal } from '@/components/iframeModal'
import { useInfiniteScroll, useVModel } from '@vueuse/core'
import { cloneDeep } from 'lodash-es'
import type { WholesaleType } from '@/enum/common'interface Props {value?: numberoptions?: RestrictGroup[]disabled?: booleanwholesaleType?: WholesaleType
}const props = defineProps<Props>()
const emit = defineEmits(['update:value'])
const bindValue = useVModel(props, 'value', emit)const controlSales = ref<RestrictGroup[]>([])
const searchedControlSales = ref<RestrictGroup[]>([])
const radioGroup = ref()
const showOptions = ref<RestrictGroup[]>([])
const count = 100
function getShowOptions() {showOptions.value = searchedControlSales.value.slice(0, count)
}
watch(() => props.options,() => {setControlSales()}
)setControlSales()
async function setControlSales() {if (props.options) {controlSales.value = props.options} else {try {controlSales.value = await getStoreGroupList({ wholesaleType: props.wholesaleType })} catch (error) {console.error(error)}}searchedControlSales.value = cloneDeep(controlSales.value)getShowOptions()
}function viewControl(storeGroupId: number) {iframeModal({url: `/provider.php/Admin/SaleControl/groupStore?store_group_id=${storeGroupId}`,fullScreen: true,title: '查看控销药店'})
}function searchControl(e: Event) {if (e.target instanceof HTMLInputElement) {const keyWord = e.target.valueif (!keyWord) {searchedControlSales.value = cloneDeep(controlSales.value)}searchedControlSales.value = controlSales.value.filter((item) => item.groupName.includes(keyWord))getShowOptions()}
}
const currentControlSale = computed(() => {return controlSales.value.find((item) => item.storeGroupId === bindValue.value)?.groupName || ''
})useInfiniteScroll(radioGroup, () => {if (showOptions.value.length >= searchedControlSales.value.length) returnshowOptions.value.push(...searchedControlSales.value.slice(showOptions.value.length, showOptions.value.length + count))
})
</script><style lang="less" scoped>
.check-group-container {padding: 16px;margin-top: 5px;max-width: 593px;max-height: 168px;overflow-y: auto;background: #fafafa;border-radius: 4px;.radios {flex-wrap: wrap;display: flex;gap: 16px 20px;}
}
</style>

4.对比

5.另外的思路

   1)后端给分页接口,返回数据总数

    2) 在useInfiniteScroll回调中记录 下一页的页码,每次追加数据直接调接口获取。

    优势初始化时,不需要等待较长时间,且不需要占用 很大的内存空间存储数据、自己过滤数据。  

三、总结

1. 使用 vue中的useInfiniteScroll可以方便的实现 滚动到 容器底部时 追加渲染数据。避免自己操作dom 

/*

希望对你有帮助!

如有错误,欢迎指正!

*/

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com

热搜词