因为业务需要,特别是列表类页面,为了前后端性能优化,前端往往需要支持分页功能。
目前实现分页主要的两种宏观方案:
固定分页组件:即分页组件固定展示在某个位置,通过点击页面需要进行分页数据展示。这种方式比较适合PC端等大屏设备,在移动端的体验不是特别好(例:ElementUI的pagination组件)
无限滚动分页:假设列表高度超过当前容器的高度,当列表底部滚动到距离浏览器窗口底部一定的距离之内时,触发下一页数据的加载。这种方案比较适合移动端,但当数据量过大的时候,需要考虑性能问题。
因为监听浏览器滚动事件非常消耗新能,并且需要格外注意防抖的问题,所以我采取了另外一种方案去实现无限滚动:
IntersectionObserver
当一个 IntersectionObserver
对象被创建时,其被配置为监听根中一段给定比例的可见区域。一旦 IntersectionObserver
被创建,则无法更改其配置,所以一个给定的观察者对象只能用来监听可见区域的特定变化值;然而,你可以在同一个观察者对象中配置监听多个目标元素。
intersectionObserver的浏览器支持情况:
直接上代码
<template>
<div class="load-more" ref="loadMoreRef">
<div v-show="loading" class="icon">
<i class="x-icon-jiazaizhong"></i>
<slot name="loading">{{loadingText}}</slot>
</div>
<div class="load" v-show="!over && !noData && !loading" @click="emit('load')">
<slot name="loadText">{{loadText}}</slot>
</div>
<div v-show="!noData && over">
<slot name="loadOverText">{{loadOverText}}</slot>
</div>
<div v-show="noData">
<slot name="noDataText">{{noDataText}}</slot>
</div>
</div>
</template>
<script setup lang="ts">
const props = defineProps({
loading: Boolean,
over: Boolean,
noData: Boolean,
loadingText: {
type: String,
default: '加载中...'
},
loadText: {
type: String,
default: '加载更多'
},
loadOverText: {
type: String,
default: '没有更多啦~'
},
noDataText: {
type: String,
default: '怎么一条数据都没有呢?'
}
})
const loadMoreRef = shallowRef();
const emit = defineEmits(['load']);
let intersectionObserver: IntersectionObserver;
let loadTimes = 0;
const maxTimes = 2;
onMounted(() => {
intersectionObserver = new IntersectionObserver(([{ intersectionRatio }]) => {
if (!!intersectionRatio && !props.loading && !props.over && !props.noData) {
if(loadTimes >= maxTimes) {
intersectionObserver.disconnect();
} else {
emit('load');
loadTimes++;
}
}
})
intersectionObserver.observe(loadMoreRef.value);
})
onBeforeUnmount(() => {
intersectionObserver.disconnect()
})
</script>
<style lang="scss">
.load-more {
padding: 40px 0 20px 0;
>.icon {
>i {
$size: 16px;
display: inline-block;
font-size: $size;
width: $size;
height: $size;
text-align: center;
line-height: $size;
animation: rotate-icon 3s linear infinite reverse;
margin-right: 5px;
}
}
> div {
width: fit-content;
margin: auto;
padding: 5px 15px;
}
>.load {
background: var(--background-color-linear-tag);
border-radius: 100px;
cursor: pointer;
color: #fff;
}
}
</style>
没错,这就是本博客当前实现无限加载的方法。