Бесконечная прокрутка в Vue 3 — реализация с useInfiniteScroll

Быстрая инструкция по реализации бесконечной прокрутки в Vue 3 с использованием @vueuse/core и JSONPlaceholder: создайте API-обёртку для получения данных, подключите useInfiniteScroll, отрисуйте список и проверьте производительность и доступность. В статье — готовые сниппеты, альтернативы и чек-листы для релиза.
Что такое бесконечная прокрутка
Бесконечная прокрутка — приём, когда приложение подгружает новые элементы по мере прокрутки страницы, вместо разбиения на пагинацию. Кратко: это удобный UX для длинных лент контента, но требует внимания к производительности, доступности и тестированию.
Краткое определение: бесконечная прокрутка динамически подгружает новые данные при достижении конца контейнера.
Важно: бесконечная прокрутка повышает вовлечённость, но ухудшает навигацию для пользователей, которые хотят быстро попасть в конец или поделиться конкретной позиции.
Основные подходы (умеренная модель)
- useInfiniteScroll из @vueuse/core — готовое и простое решение для Vue 3.
- IntersectionObserver + собственная логика — гибкость и контроль.
- Виртуализация (vue-virtual-scroller) — для очень больших наборов данных.
Выбор зависит от объёма данных, требований к производительности и потребности в управлении состоянием.
Настройка приложения Vue
Чтобы выполнить примеры, требуется базовое понимание Vue 3, JavaScript и работы с HTTP (axios). Для начала создайте новое приложение Vue:
npm create vue
При настройке проекта выберите No для всех дополнительных функций — в этом руководстве мы подключим только нужные пакеты вручную.

Перейдите в директорию приложения и установите пакеты:
npm install axios @iconify/vue @vueuse/core
Установятся три пакета: axios (HTTP-запросы), @iconify/vue (иконки) и @vueuse/core (утилиты Vue, включая useInfiniteScroll).
Получение тестовых данных из JSONPlaceholder
Для примера мы используем публичный фейковый API JSONPlaceholder. В production вы будете работать с собственным API.
Создайте в src папку api и файл getComments.js со следующим кодом:
//getComments.js
import axios from"axios";
asyncfunctiongetComments(max, omit) {
try {
const comments = await axios.get(
`https://jsonplaceholder.typicode.com/comments?_limit=${max}&_start=${omit}`
);
return comments.data.map((comment) => comment.body);
} catch (error) {
console.error(error);
}
}
exportdefault getComments;
Коротко: функция getComments(max, omit) делает GET-запрос и возвращает массив строк — тел комментариев.
Советы по API:
- Всегда обрабатывайте ошибки и возвращайте понятный ответ на клиенте.
- Подумайте о кэшировании и стратегиях повторных попыток (retry) для нестабильных сетей.
Создание компонента InfiniteScroll
Создайте src/components/InfiniteScroll.vue и добавьте скрипт-логику (оставляем код как есть):
Пояснения к коду:
- listEl — ссылка на DOM-элемент, за которым следит useInfiniteScroll.
- commentsDisplayed — сколько элементов загружаем за запрос (из примера 20).
- commentsList загружается сразу при монтировании через await getComments(…).
- commentsToDisplayOnScroll подгружает и добавляет новые элементы.
- useInfiniteScroll принимает элемент, функцию-колбэк и опции (distance — расстояние в пикселях от конца, при котором запускается загрузка).
Важно: начальная загрузка через await в setup возможна благодаря
Шаблон компонента и использование
Добавьте шаблон для отображения списка:
-
{{ comment }}
Подключите компонент в App.vue:
Запустите приложение:
npm run devПревью приложения (при настройках из примера):

Альтернативный подход: IntersectionObserver вручную
Когда нужен полный контроль над поведением (например, поддержка сложной логики загрузки), используйте IntersectionObserver вместо готовой утилиты.
Пример простого хука для Vue 3:
// useIntersectionLoad.js
import { ref, onMounted, onBeforeUnmount } from 'vue'
export function useIntersectionLoad(targetRef, callback, options = {}) {
const observer = ref(null)
onMounted(() => {
if (!targetRef.value) return
observer.value = new IntersectionObserver(async (entries) => {
for (const entry of entries) {
if (entry.isIntersecting) {
await callback()
}
}
}, options)
observer.value.observe(targetRef.value)
})
onBeforeUnmount(() => {
if (observer.value && targetRef.value) {
observer.value.unobserve(targetRef.value)
}
})
}Плюсы: гибкость, контроль над threshold и rootMargin. Минусы: чуть больше кода и ответственности за edge-cases.
Когда бесконечная прокрутка не подходит
- Контенту нужна прямая нумерация страниц или конкретные URL для каждой позиции (SEO/поделиться ссылкой).
- Пользователям важна возможность быстро перейти к концу списка.
- Сильные требования к доступности без доп. усилий.
В этих случаях лучше использовать явную пагинацию или комбинированный подход “пагинация + подгрузка”.
Проблемы производительности и оптимизация
- Растущий DOM. Решение: виртуализация элементов (vue-virtual-scroller). При больших объёмах DOM-элементов виртуализация критична.
- Частые ререндеры. Решение: мемоизация/ключи, минимизация реактивных зависимостей.
- Сетевые задержки. Решение: кеширование, optimistic UI, prefetch следующей страницы.
- Дублирующие запросы. Решение: блокировка повторных запросов (in-flight guard).
Рекомендации:
- Параллельно загружайте не более одного запроса.
- Используйте throttle/debounce для обработчиков прокрутки, когда не используете IntersectionObserver.
- Сжимайте payload на сервере: отдавайте только нужные поля.
Доступность
- Обеспечьте фокусируемую структуру и логичную последовательность чтения для экранных читалок.
- Добавьте кнопку “Загрузить ещё” как альтернативный способ подгрузки — это полезно для пользователей клавиатуры и скринридеров.
- Помечайте динамические контейнеры ARIA-ролями (role=”list” / role=”listitem”) при необходимости.
- Обеспечьте возможность перейти к конкретному элементу (anchor links) или показать счётчик загруженных элементов.
Критерии приёмки
- Компонент загружает первую порцию данных при старте.
- При прокрутке до нижней границы контейнера подгружаются новые данные.
- Повторная прокрутка не вызывает параллельных дублирующих запросов.
- В случае ошибки отображается информативное сообщение и кнопка для повтора.
- Тесты покрывают случаи: успешная загрузка, пустой результат, ошибка сети.
Чек-лист перед релизом
Для разработчика:
- Обработаны ошибки запросов.
- Есть индикаторы загрузки и fallback-контент.
- Защита от повторных запросов (in-flight guard).
- Тестовые сценарии написаны и проходят.
Для QA:
- Протестирован сценарий низкой и высокой скорости сети.
- Проведены тесты на мобильных устройствах.
- Проверено поведение при пустом ответе API.
Для дизайнера:
- Есть визуальный индикатор загрузки.
- Есть альтернативный способ навигации (кнопка «Загрузить ещё»).
Для менеджера продукта:
- Утверждён лимит на количество элементов за запрос.
- Определена стратегия кэширования и TTL.
Тесты и сценарии приёма
- Успешная первая загрузка: список содержит N элементов после монтирования.
- Подгрузка после скролла: после прокрутки список увеличивается на N элементов.
- Поведение при ошибке: показывается сообщение и кнопка «Повторить».
- Кнопка «Загрузить ещё» работает как резервный механизм.
Шаблон быстрых настроек (cheat sheet)
- Инициализация: commentsDisplayed = 20
- Триггер загрузки: distance: 10 (px)
- Защита от дублирования: isLoading flag
- Адаптация под мобильные: уменьшить payload, lazy-load изображений
Типичные паттерны ошибок и как их исправлять
- Дублирование элементов: используйте уникальные ключи и guard для in-flight запросов.
- Неполная подгрузка: убедитесь, что вы правильно передаёте параметры start/limit.
- Плохая производительность: включите виртуализацию и уменьшите количество DOM-узлов.
Мини‑методология внедрения
- Начните с простой реализации useInfiniteScroll и JSONPlaceholder.
- Протестируйте UX и измерьте рост DOM‑узлов.
- При необходимости добавьте виртуализацию.
- Добавьте кеширование и retry-логику.
- Покройте провальные сценарии тестами и QA.
Рекомендации по миграции с пагинации
- Сохраняйте URL-идентификатор страницы в истории (history.pushState), если пользователям важно делиться ссылками.
- Рассмотрите гибрид: пагинация на сервере + подгрузка на клиенте для UX, но с URL для навигации.
Конфиденциальность и обработка данных
- Не подгружайте личные данные без явной необходимости.
- Для персонализированного контента используйте защищённые эндпоинты и токены.
- Сообщайте пользователю, если подгружается персонализированный контент.
Примеры альтернативных библиотек и инструментов
- vue-virtual-scroller — для виртуализации больших списков.
- react-virtualized / react-window — примеры для React, если мигрируете стек.
- SWR / React Query / Vue Query — для управления кешем и состоянием загрузки.
Итоговая сводка
Бесконечная прокрутка в Vue 3 — удобная техника для лент контента. Для большинства задач достаточно @vueuse/core и useInfiniteScroll. При росте объёма данных учитывайте виртуализацию, защиту от дублирующих запросов и доступность. Тестируйте поведение на разных сетях и устройствах, добавляйте резервную кнопку «Загрузить ещё» для улучшения UX.
Важно: выберите подход, исходя из требований к навигации, SEO и доступности.
Спасибо за чтение — примените подход в своём проекте и протестируйте на реальных данных.
Похожие материалы
Как разделить меш в Blender
Как увеличить изображение без потери качества
Как создать влог на iPhone — полное руководство
Как отразить экран на телевизор — все способы
Бесконечная прокрутка в Vue 3 — useInfiniteScroll