Пагинация: offset, cursor, infinite scroll

↑ 8.4 Frontend System Design · ← Предыдущая · → Следующая

Желательно~3 мин чтенияУровень: senior

Зачем это на собесе

Один из самых частых блоков в дизайне лент и таблиц.

Способы

СпособЗапросПлюсыМинусы
Offset/limit?offset=40&limit=20 (или page)произвольный переход на страницу, простодубли и пропуски при вставках, медленно на больших offset (БД), нужен total
Cursor (keyset)?after=<cursor>&limit=20стабильно при изменениях, быстро на больших данныхнельзя прыгнуть на страницу N
Time-based / id-based?since=tsинкрементальная загрузкапривязка к порядку

Cursor

Ответ содержит nextCursor; курсор — непрозрачная строка (закодированные значения сортировки + id).

{ "items": [...], "nextCursor": "eyJpZCI6MTIzfQ", "hasMore": true }

Клиентские паттерны

  • Страницы (номера): таблицы админки, нужен переход по номеру;
  • «Показать ещё»: явное действие, хороший контроль;
  • Infinite scroll: IntersectionObserver на sentinel, подгрузка при приближении к концу;
  • Виртуализация при больших массивах в DOM;
  • Двунаправленная (чат): подгрузка вверх с сохранением позиции.
const { data, fetchNextPage, hasNextPage } = useInfiniteQuery({
  queryKey: ['feed'],
  queryFn: ({ pageParam }) => api.feed({ cursor: pageParam }),
  initialPageParam: undefined as string | undefined,
  getNextPageParam: (last) => last.nextCursor,
})

Нюансы infinite scroll

  • потеря позиции при возврате (восстановление скролла, кэш);
  • доступ к футеру и поиск по странице;
  • доступность: клавиатура, объявление загрузки;
  • дедупликация элементов, если данные сдвинулись;
  • индикаторы: loading, пусто, ошибка с повтором;
  • SEO: страницы с URL для индексации.

Как выбрать

СценарийВыбор
Таблица с переходом по номеру, стабильные данныеoffset
Лента, чат, активные вставкиcursor
Мобильный списокinfinite scroll + cursor
Отчёт с фильтрамисерверные фильтры + offset/cursor

Вопросы с ответами