← Назад к списку
Системный дизайнFrontendSenior

Спроектируйте бесконечную ленту (infinite scroll) с тысячами карточек: загрузка данных, виртуализация, сохранение позиции.

Короткий ответ

  • Пагинация курсором надёжнее offset при живой ленте
  • Подгрузка через IntersectionObserver по элементу-стражу
  • Виртуализация: рендерить только видимое окно плюс запас
  • Переменная высота карточек — измерение и кэш высот
  • Восстановление позиции при возврате к ленте
  • Состояния: скелетоны, ошибка страницы с повтором, конец ленты
  • Доступность: фокус, «вернуться наверх», усталость от бесконечности

Бесконечная лента держится на курсорной пагинации, дозагрузке через IntersectionObserver и виртуализации списка, а зрелость решения проверяется восстановлением позиции скролла и обработкой ошибок дозагрузки.

Как сказать вслух

пример ответа

Сначала данные: беру пагинацию по курсору, а не по номеру страницы — в живой ленте элементы добавляются, и офсеты начинают дублировать записи. Подгрузку следующей порции запускаю через IntersectionObserver по невидимому элементу внизу, с запасом до конца списка. Чтобы DOM не распухал на тысячах карточек, применяю виртуализацию: в документе живут только видимые элементы плюс буфер, остальное — пустые отступы. Отдельно продумываю возврат из карточки обратно в ленту с сохранением позиции и честные состояния: скелетоны, ошибку дозагрузки с кнопкой повтора, конец ленты.

Подробный ответ

Основной ответ

Данные: API с курсором (cursor/nextToken) стабилен при вставках в начало ленты; ответ несёт items, nextCursor и признак конца. Дозагрузка: страж-элемент с IntersectionObserver и rootMargin в несколько сотен пикселей, чтобы грузить заранее; обязательна защита от параллельных запросов (флаг isFetching) и ретрай при ошибке — иначе лента молча «обрывается». Виртуализация: окно видимых элементов вычисляется от scrollTop, высота списка имитируется контейнером-распоркой; при переменной высоте карточек используют оценку с дозамером после рендера и кэш измеренных высот (готовые решения — react-window, TanStack Virtual). Изображения — lazy loading с заданными размерами против сдвигов макета (CLS). Возврат к ленте: сохранять позицию и загруженные данные в кэше состояния или sessionStorage, иначе пользователь теряет место — одна из самых раздражающих ошибок лент. UX-зрелость: кнопка «наверх», сохранение доступа к футеру, «Load more» вместо автоскролла там, где важна достижимость конца, живые области для скринридеров.

Ключевые моменты

  • Курсор против offset. При вставках в живую ленту offset-пагинация дублирует и пропускает элементы; курсор от последнего элемента стабилен.
  • Виртуализация. DOM на десятки тысяч узлов убивает скролл и память; рендер окна с буфером держит список быстрым.
  • Восстановление позиции. Переход в карточку и назад должен возвращать к тому же месту — это требует кэша данных и позиции скролла.
  • Отказоустойчивость. Ошибка дозагрузки с видимым повтором и защита от дублирующихся запросов отличают продакшен от демо.

Практический контекст

Классическая system-design-задача про производительность DOM и работу с данными. Интервьюер ждёт поэтапного рассуждения: сначала наивное решение, затем узкие места (распухание DOM, гонки подгрузки, потеря позиции) и их лечение. Сильный сигнал — обсуждение границ: когда бесконечная лента вообще неуместна и честная пагинация лучше, например в каталоге с фильтрами и ссылками на страницы.

Частые ошибки

  • Предлагают offset-пагинацию, не замечая дублей при живых вставках в ленту
  • Рендерят все загруженные карточки в DOM без виртуализации
  • Забывают про возврат к позиции скролла и ошибку дозагрузки

ИП Кочкин Алексей Сергеевич · ИНН 390509026279 · ОГРНИП 325390000030973 · jiniys2005@yandex.ru