Skip to content

infiniteScroll 无限滚动指令

当滚动到容器底部时自动触发加载函数,实现无限滚动列表效果。

安装与引入

按需导入(推荐)

ts
import { VInfiniteScroll } from '@cuixingjian/cui-utils'

全局注册

ts
import { createApp } from 'vue'
import { VInfiniteScroll } from '@cuixingjian/cui-utils'

const app = createApp(App)
app.directive('infinite-scroll', VInfiniteScroll)
app.mount('#app')

局部注册

vue
<script setup lang="ts">
import { VInfiniteScroll as vInfiniteScroll } from '@cuixingjian/cui-utils'
</script>

<template>
  <div v-infinite-scroll="loadMore" style="overflow: auto; height: 400px;">
    <div v-for="item in list" :key="item">{{ item }}</div>
  </div>
</template>

效果演示

基本用法

滚动到底部自动加载更多数据。

使用代码

基本用法:

vue
<script setup lang="ts">
import { VInfiniteScroll as vInfiniteScroll } from '@cuixingjian/cui-utils'

const loadMore = () => {
  // 加载更多数据
  console.log('加载更多')
}
</script>

<template>
  <div v-infinite-scroll="loadMore" style="overflow: auto; height: 400px;">
    <!-- 列表内容 -->
  </div>
</template>

配置选项:

vue
<script setup lang="ts">
import { ref } from 'vue'
import { VInfiniteScroll as vInfiniteScroll } from '@cuixingjian/cui-utils'

const loading = ref(false)

const loadMore = () => {
  loading.value = true
  // 加载数据...
  setTimeout(() => {
    loading.value = false
  }, 1000)
}
</script>

<template>
  <div 
    v-infinite-scroll="loadMore"
    :infinite-scroll-disabled="loading"
    :infinite-scroll-distance="50"
    :infinite-scroll-delay="300"
    style="overflow: auto; height: 400px;"
  >
    <!-- 列表内容 -->
  </div>
</template>

使用对象配置:

vue
<script setup lang="ts">
const options = {
  disabled: false,
  distance: 100,
  immediate: true,
  delay: 200
}

const loadMore = () => {
  console.log('加载更多')
}
</script>

<template>
  <div v-infinite-scroll="{ ...options, callback: loadMore }">
    <!-- 列表内容 -->
  </div>
</template>

API 说明

指令值类型

类型说明示例
Function滚动到底部时触发的回调函数v-infinite-scroll="loadMore"
Object传入配置对象(需包含 callback)v-infinite-scroll="{ disabled: false }"

配置选项 (InfiniteScrollOptions)

参数类型默认值说明
disabledbooleanfalse是否禁用指令
distancenumber0触发加载的距离阈值(像素)
immediatebooleantrue是否立即检查(内容不足一屏时)
delaynumber200防抖延迟时间(毫秒)

指令修饰符

通过 HTML 属性传递配置:

属性类型说明
infinite-scroll-disabledboolean是否禁用
infinite-scroll-distancenumber距离阈值
infinite-scroll-immediateboolean是否立即检查
infinite-scroll-delaynumber延迟时间

使用场景

  • 新闻列表 - 用户滚动浏览新闻时,自动加载下一页内容,提供流畅的阅读体验
  • 图片瀑布流 - 在图片展示页面,滚动到底部时自动加载更多图片,适合相册、作品集等场景
  • 聊天记录 - 向上滚动查看历史消息时,自动加载更早的聊天记录
  • 商品列表 - 电商网站的商品浏览页面,滚动加载更多商品
  • 社交动态 - 类似微博、朋友圈的信息流,持续加载新内容
  • 搜索结果 - 搜索结果页面的分页加载,提升用户体验

注意事项

容器要求

  • 必须设置 overflow: autooverflow-y: auto
  • 必须设置固定高度
  • 容器必须是可滚动的

防止重复加载

使用 disabled 选项在加载时禁用指令:

vue
<script setup lang="ts">
const loading = ref(false)
const disabled = computed(() => loading.value || noMore.value)
</script>

<template>
  <div 
    v-infinite-scroll="load"
    :infinite-scroll-disabled="disabled"
  >
    <!-- 内容 -->
  </div>
</template>

立即检查

immediate: true 时,指令会在挂载后立即检查一次,如果内容不足一屏会自动触发加载。

性能优化

  • 使用 delay 选项设置防抖延迟,避免频繁触发
  • 使用 distance 选项提前触发加载,提升用户体验
  • 监听内容变化,自动重新检查(使用 ResizeObserver)

最佳实践

vue
<script setup lang="ts">
import { ref, computed } from 'vue'

const list = ref([])
const loading = ref(false)
const noMore = ref(false)

// 组合禁用条件
const disabled = computed(() => loading.value || noMore.value)

const loadMore = async () => {
  if (disabled.value) return
  
  loading.value = true
  try {
    const data = await fetchData()
    if (data.length === 0) {
      noMore.value = true
    } else {
      list.value.push(...data)
    }
  } catch (error) {
    console.error('加载失败', error)
  } finally {
    loading.value = false
  }
}
</script>

<template>
  <div 
    v-infinite-scroll="loadMore"
    :infinite-scroll-disabled="disabled"
    :infinite-scroll-distance="50"
    :infinite-scroll-delay="200"
    style="height: 500px; overflow: auto;"
  >
    <div v-for="item in list" :key="item.id">
      {{ item.name }}
    </div>
    <div v-if="loading">加载中...</div>
    <div v-if="noMore">没有更多了</div>
  </div>
</template>

源码展示

展开查看完整源码
ts
import type { Directive } from 'vue'
import { withInstallDirective } from '../install'

export interface InfiniteScrollOptions {
  disabled?: boolean
  distance?: number
  immediate?: boolean
  delay?: number
}

type BindingValue = (() => void) | InfiniteScrollOptions

// 详见 packages/utils/directives/infiniteScroll.ts
export const VInfiniteScroll = withInstallDirective(infiniteScroll, 'infinite-scroll')