Skip to content

InfiniteLoader

Оборачивает VirtualList и вызывает onLoadMore, когда пользователь прокручивает в пределах threshold пикселей от низа (или верха, либо обоих).

Пропы

items

T[]

Текущий массив данных.

onLoadMore

() => Promise<void>

Вызывается, когда нужны дополнительные данные.

isLoading

boolean

Выполняется ли загрузка в данный момент.

hasMore

boolean

Доступны ли ещё данные.

threshold

number · по умолчанию: 200

Расстояние от края (px), при котором срабатывает onLoadMore.

direction

'down' | 'up' | 'both' · по умолчанию: 'down'

Какой(-ие) край(-я) запускают загрузку.

estimatedItemSize

number · по умолчанию: 50

Оценочная высота строки.

overscan

number · по умолчанию: 3

Дополнительные строки, рендерящиеся вне вьюпорта.

keyField

string · по умолчанию: 'id'

Поле ключа строки.

motionBlur

boolean · по умолчанию: false

Применить CSS-размытие, масштабируемое по скорости прокрутки при быстрой прокрутке.

Слоты

default

Область видимости: { item: T, index: number, style }

Содержимое строки.

loading-indicator

Область видимости: нет

Кастомный индикатор загрузки (показывается сверху/снизу в зависимости от direction).

empty

Область видимости: нет

Пустое состояние.

Emits

scroll

Payload: Event

visible-range-change

Payload: { start: number; end: number }

Публичный API

scrollTo(index, align?, options?)

Прокрутить к элементу. align'start' | 'center' | 'end' | 'auto'.

scrollToOffset(px, options?)

Прокрутить к пиксельному смещению.

getScrollElement()

Возвращает элемент, который реально прокручивается — сочетайте с VirtualScrollbar.

ts
loaderRef.value?.scrollTo(index, align)
loaderRef.value?.scrollTo(index, 'start', { behavior: 'smooth' })
loaderRef.value?.scrollToOffset(px)
loaderRef.value?.getScrollElement()

Пример

vue
<script setup lang="ts">
import { ref } from 'vue'
import { InfiniteLoader } from 'vue-virtual-scroller-kit'

interface Post {
  id: number
  title: string
}

const posts = ref<Post[]>([])
const isLoading = ref(false)
const hasMore = ref(true)
let page = 0

async function loadMore() {
  if (isLoading.value || !hasMore.value) return
  isLoading.value = true
  try {
    const res = await fetch(`/api/posts?page=${page}`)
    const data: Post[] = await res.json()
    posts.value = [...posts.value, ...data]
    hasMore.value = data.length === 20
    page++
  } finally {
    isLoading.value = false
  }
}

await loadMore()
</script>

<template>
  <InfiniteLoader
    :items="posts"
    :on-load-more="loadMore"
    :is-loading="isLoading"
    :has-more="hasMore"
    :estimated-item-size="72"
    style="height: 600px"
  >
    <template #default="{ item }">
      <div class="post-row">{{ item.title }}</div>
    </template>
    <template #loading-indicator>
      <div style="padding: 16px; text-align: center">Loading…</div>
    </template>
  </InfiniteLoader>
</template>