Как реализовать бесконечную ленту (infinite scroll) в веб-приложении
Что проверяют
Умеете ли вы сделать ленту, которая не тормозит на тысячном элементе, не грузит одну страницу дважды и не ломает навигацию. Следом обычно спрашивают, как вернуть пользователя на то же место после перехода в карточку и обратно.
Как отвечать
- триггер загрузки:
IntersectionObserverна пустом элементе-сторожке в конце списка, сrootMargin: "400px", чтобы следующая порция начинала грузиться заранее. Обработчикscrollс ручным расчётом высоты хуже: он срабатывает десятки раз в секунду и требует троттлинга; - состояние запроса: флаги
isLoadingиhasMore. Пока запрос идёт, повторный не отправляется, при пустом ответе наблюдатель отключается; - пагинация на сервере: курсорная,
?cursor=<id последнего>&limit=20, а неoffset. Приoffsetновые записи сверху сдвигают выдачу, и в ленте появляются дубли или пропуски; - виртуализация: в DOM держатся только видимые элементы плюс запас. Для React это
@tanstack/react-virtualилиreact-window, без библиотек помогает CSScontent-visibility: auto; - возврат назад: загруженные страницы и позицию прокрутки храните в кэше данных или
sessionStorage, курсор можно отражать в URL; - ошибки: при сбое показывается кнопка «Повторить», а не вечный спиннер.
Отдельно назовите компромиссы. Подвал сайта становится недостижим, поэтому его убирают или переходят на кнопку «Показать ещё». Поисковый робот страницу не прокручивает, так что для индексации нужны обычные постраничные URL. Для доступности подходят role="feed" и aria-busy на время загрузки.
Где спотыкаются
Проверяют конец страницы в scroll без флага загрузки. Пока первый запрос в полёте, событие успевает сработать ещё несколько раз, и одна страница приходит трижды.
Не думают о короткой первой порции. Если она не заполняет экран, прокрутки нет, и scroll не наступает никогда. У IntersectionObserver похожая ловушка: сторожок остался видимым, пересечение не изменилось, колбэк не вызвался. После загрузки проверяйте, виден ли он ещё, и догружайте.
Бесконечно наращивают DOM. Через пару сотен сложных карточек прокрутка на телефоне начинает дёргаться, а память вкладки растёт, пока браузер её не выгрузит.