Нескінченне прокручування в мобільному застосунку: правильна реалізація

Стрічка, яка смикається та дублює картки, втрачає користувачів швидше, ніж будь-яка інша помилка інтерфейсу. Ми проєктуємо та впроваджуємо коректне підвантаження контенту під ключ: від аудиту поточної стрічки до передачі готового рішення команді. Зменшуємо кількість збоїв під час прокручування та прискорюємо видачу контенту без втрати позиції екрана.

Розробка та підтримка будь-яких видів мобільних додатків:

Інформаційні та розважальні мобільні програми
Новинки, ігри, довідники, онлайн-каталоги, погодні, фітнес та здоров'я, туристичні, освітні, соціальні мережі та месенджери, квіз, блоги та подкасти, форуми, агрегатори
Мобільні програми електронної комерції
Інтернет-магазини, B2B-додатки, маркетплейси, онлайн-обмінники, кешбек-сервіси, біржі, дропшиппінг-платформи, програми лояльності, доставка їжі та товарів, платіжні системи
Мобільні програми для управління бізнес-процесами
CRM-системи, ERP-системи, управління проектами, інструменти для команди продажів, облік фінансів, управління виробництвом, логістика та доставка, управління персоналом, системи моніторингу даних
Мобільні програми електронних послуг
Дошки оголошень, онлайн-школи, онлайн-кінотеатри, платформи надання електронних послуг, платформи кешбеку, відеохостинги, тематичні портали, платформи онлайн-бронювання та запису, платформи онлайн-торгівлі

Це лише деякі з типів мобільних додатків, з якими ми працюємо, і кожен із них може мати свої специфічні особливості та функціональність, а також бути адаптованим під конкретні потреби та цілі клієнта.

Послуги, які ми пропонуємо
Показано 1 з 1Усі 1734 послуг
Нескінченне прокручування в мобільному застосунку: правильна реалізація
Середній
від 1 дня до 3 днів
Часті запитання

Infinite scroll у мобільному застосунку: де ламається стрічка

Infinite scroll виглядає як простий прийом: користувач доходить до низу стрічки контенту — застосунок підвантажує наступну порцію.

На практиці нескінченне прокручування ламається в чотирьох передбачуваних місцях, і кожне з них замовник бачить як «застосунок гальмує» або «картки задвоюються».

Перший збій — дублі карток. Підвантаження даних смикається за кількома подіями одразу: скрол, оновлення списку, повернення з фону. Два запити йдуть паралельно, відповіді приходять у різному порядку, і у стрічці з'являються однакові елементи.

Лікується це не «захистом від дурня», а єдиним джерелом правди: сторінки нумеруються, відповіді склеюються за ідентифікатором, а повторний запит блокується до завершення попереднього.

Другий — втрата позиції скролу. Користувач відкриває картку, повертається — і стрічка стрибає на початок або на випадкове місце. Причина в тому, що список перезбирається з нуля, а збережена позиція не прив'язана до конкретного елемента.

Правильно зберігати якір: ідентифікатор видимої картки та зсув усередині неї, тоді повернення стає рівно туди, де користувач зупинився.

Третій — ривки під час швидкого прокручування. Поки дані вантажаться, у стрічці порожнеча або підказка завантаження, і палець проскакує повз ще не відрендерені картки.

Тут працює передзавантаження за екран і заглушки-скелетони фіксованої висоти: список не змінює геометрію, прокручування залишається плавним.

Четвертий — витоки пам'яті на довгій сесії. Година гортання — і в пам'яті висять тисячі відрендерених карток і підвислих запитів. Рішення — перевикористання елементів і скасування запитів, які вже не потрібні.

Розбираємо, як це закривається в реальній розробці.

Що дає коректна реалізація підвантаження контенту

Коректно зроблений infinite scroll — це не «стрічка, яка просто докручується», а передбачувана система підвантаження: сторінка запитується заздалегідь, до того як користувач дійде до краю, елементи не дублюються при повторних запитах, а пам'ять не зростає лінійно з глибиною прокрутки.

Різниця видна в метриках: відгук застосунку залишається рівним на сотнях екранів униз, а не деградує до третьої-четвертої сотні елементів.

Перший вимірюваний ефект — стабільність списку. Коли ключі елементів унікальні, а вставка нових сторінок відбувається без перезбирання вже відрендерених, зникають дублі та «стрибки» позиції при поверненні на екран.

Користувач, який згорнув застосунок на середині стрічки й повернувся через годину, опиняється там само, де зупинився, — частка відмов при поверненні падає, бо екран не перезавантажується з нуля.

Другий ефект — передбачуване споживання пам'яті. Стрічка утримує в пам'яті вікно навколо видимої області, а не всі завантажені елементи: відпрацьовані йдуть із дерева, кеш зображень обмежений за розміром.

На довгих сесіях це знімає зростання споживання та пов'язані з ним примусові вивантаження застосунку системою.

Третій — рівний відгук застосунку на прокрутку. Підвантаження наступної сторінки запускається із запасом, паралельно з інерцією скролу, а не в момент упору в низ; при повільній мережі показується скелетон, а не порожній екран.

Разом це дає продуктивність стрічки, яка не залежить від того, як довго користувач гортає, — і саме її ми закладаємо в реалізацію.

Варіанти підвантаження: сторінки, курсор, вікно перегляду

Є три способи віддавати дані в стрічку, і вибір між ними визначає, як застосунок поводитиметься на швидкому скролі та за живих правок контенту.

Варіант Коли підходить Обмеження
Посторінкове завантаження зі зміщенням Статичні каталоги, рідкісні вставки, прості списки Під час додавання записів на початок стрічки елементи дублюються й «стрибають»: зміщення сторінок рахується від початку, а воно вже зсунулося
Курсорна пагінація Живі стрічки, чати, маркетплейси з частими правками Потрібен стабільний ключ сортування й підтримка курсора на боці сервісу; перехід на довільну сторінку неможливий
Вікно перегляду Обмежений набір даних, який уже надійшов цілком Пам'ять зростає разом з обсягом, а свіжі записи не підтягуються без повторного запиту

Посторінкове завантаження зі зміщенням — найшвидший старт: сервер віддає зріз за номером сторінки, клієнт просто збільшує лічильник. Проблема проявляється під навантаженням: поки користувач гортає, на початок стрічки надходять нові записи, і наступна сторінка віддає вже побачене.

В інтерфейсі це дублі карток і загублені елементи — найпомітніше у стрічках із частими публікаціями.

Курсорна пагінація тримає позицію за останнім отриманим елементом, а не за його порядковим номером. Вставки й видалення вище курсора не зсувають вибірку, тому дублів немає навіть за активних правок.

Ціна — серверна логіка: потрібен стабільний ключ сортування, а перехід «одразу на середину» списку стає неочевидним.

Вікно перегляду працює інакше: дані надходять одним набором, а на екран потрапляє лише видима частина. Це знімає навантаження з сервера, але перекладає його на пам'ять пристрою — на великих обсягах застосунок починає гальмувати.

Передзавантаження наступної порції за 1-2 екрани до кінця списку згладжує очікування, але не скасовує обмежень обраної схеми.

Етапи впровадження: від аудиту стрічки до передачі команді

Впровадження infinite scroll починається не з коду, а з аудиту стрічки: дивимося, як застосунок зараз віддає дані, де кешуються сторінки, які запити йдуть під час швидкого скролу.

На цьому кроці стає видно, що саме гальмує — розмір відповіді, відсутність пагінації на бекенді або зайві перемальовування списку. Без аудиту будь-яке налаштування перетворюється на підгонку симптомів.

Послідовність робіт

  1. Аудит стрічки. Розбираємо поточний потік даних: скільки запитів іде на один екран, як поводиться застосунок при втраті мережі, що відбувається з пам'яттю при довгому прокручуванні. Фіксуємо вузькі місця у звіті.
  2. Схема завантаження. Проєктуємо контракт: розмір сторінки, курсор замість зміщення, поведінка при дубльованих записах, стратегія кешу та повторних спроб. Схема узгоджується з бекендом — інакше клієнт і сервер почнуть розходитися в очікуваннях.
  3. Налаштування. Реалізуємо підвантаження з випереджальним запитом за 1-2 екрани до кінця списку, індикатором завантаження та захистом від паралельних однакових запитів. Окремо закриваємо сценарії: порожня відповідь, помилка мережі, кінець стрічки.
  4. Тестування прокручування. Прогоняємо на реальних сценаріях — повільна мережа, різкий флік до низу, повернення з фону, поворот екрана. Перевіряємо, що позиція зберігається, а пам'ять не зростає нескінченно.
  5. Передача коду. Віддаємо команді замовника репозиторій зі змінами, опис контракту завантаження, інструкцію щодо правки розміру сторінки та чек-лист перевірок перед релізом.

Кожен етап закінчується артефактом: звіт аудиту, схема, робочий код, результати тестів, документація. Це дозволяє підключати вашу команду на будь-якому кроці й не залежати від одного виконавця. Опишіть задачу — повернемося з оцінкою та планом за етапами.

Артефакти: конфігурації, схеми станів, регламенти

Розробка стрічки не закінчується робочим екраном. Щоб застосунок можна було розвивати, передавати іншій команді та безпечно змінювати поведінку підвантаження, ми фіксуємо рішення в артефактах — від моделі даних до регламенту супроводу. Нижче — що саме залишається у вас на руках після здачі.

  • Конфігурація стрічки — опис параметрів підвантаження: розмір сторінки, поріг спрацювання довантаження, поведінка при порожній відповіді та при вичерпанні даних. Змінюється без переписування екрана.
  • Схема станів — діаграма переходів між «завантаження», «дані є», «порожньо», «помилка», «кінець списку» та правил повернення з кожного стану. За нею видно, де саме стрічка може «залипнути».
  • Правила кешування — що і на який термін зберігається локально, як інвалідується при оновленні, як поводиться застосунок без мережі та при поверненні з фону.
  • Модель даних — структура елемента списку, ключі пагінації, правила дедуплікації при повторному підвантаженні та злитті сторінок.
  • Документація — текстовий опис логіки підвантаження, точок розширення та типових сценаріїв зміни: новий фільтр, зміна джерела, додавання типу картки.
  • Регламент супроводу — що перевіряти при релізі, які метрики дивитися (частка помилок довантаження, час до першого екрана, глибина прокрутки), як відкочувати зміни стрічки.
  • Схеми інтеграції — як стрічка пов'язана з аналітикою, пошуком та офлайн-режимом, щоб нові модулі не ламали пагінацію.

Ці артефакти знімають головний ризик підтримки: логіка підвантаження перестає бути знанням одного інженера. Опишіть задачу — повернемося з оцінкою та планом, включно зі складом документації під ваш процес.

Кейс: як стрічка перестала дублювати картки

Замовник прийшов із класичною скаргою: користувачі бачать одні й ті самі картки у стрічці, а під час швидкого прокручування вниз список на секунди завмирає.

Метрики підтвердили діагноз: частка дублів сягала 8% показів, відгук на швидкий свайп просідав до 400–600 мс, а пам'ять на довгій сесії зростала лінійно — застосунок доходив до 700 МБ і система починала вивантажувати процес у фоні.

Що було під капотом

Стрічка тягла сторінки за номером offset, а ключем елемента слугував індекс у масиві. Варто було даним на сервері перевпорядкуватися між запитами — і той самий товар приїжджав двічі під різними індексами, а частина позицій зникала.

Кеш тримав усі завантажені сторінки в пам'яті без витіснення, тому довга сесія перетворювалася на утечу.

Що змінили

Перейшли на курсорну пагінацію з непрозорим токеном замість номера сторінки — сервер віддає «наступний після ось цього», і вставки в середину більше не зсувають вікно.

Ключем елемента зробили стабільний ідентифікатор запису, а не позицію: повторний прихід тієї самої сутності тепер схлопується на рівні сховища.

Додали вікно утримання — тримаємо в пам'яті поточний екран і по парі екранів у кожен бік, решта витісняється, а картинки вантажаться в міру наближення до межі.

Що стало

Частка дублів упала до нуля — повторні покази ловляться на вході. Відгук на швидке прокручування скоротився до 80–120 мс, бо підвантаження йде заздалегідь, за екран до краю, і не блокує кадр.

Пам'ять на довгій сесії стабілізувалася в районі 180–220 МБ і перестала зростати з часом: витіснення працює незалежно від того, скільки користувач гортає.

Окремо закрили відкат: якщо сервер поверне застарілий токен, стрічка не рветься, а тихо перезапитує вікно з останньої валідної позиції. Кейс маркетплейсу показав головне — infinite scroll ламається не на анімації, а на моделі даних під нею.

Чим курсорна пагінація відрізняється від сторінкової

Сторінкова пагінація спирається на зсув: клієнт запитує «перші 20», потім «наступні 20» — сервер відраховує записи від початку вибірки. Поки дані статичні, все передбачувано. Але стрічка жива: між запитами надходить новий пост, і все, що нижче, зсувається вниз на одну позицію.

Наступна сторінка починається вже не з того запису — користувач бачить дублікат або втрачає елемент, який так і не показався. Це не баг клієнта, а властивість самого зсуву: він не прив'язаний до конкретного запису, лише до його порядкового номера в моменті.

Курсорна пагінація замість номера сторінки передає вказівник на останній отриманий запис — його унікальний ключ і значення поля сортування. Сервер повертає все, що йде строго після цієї точки.

Вставка нових записів зверху на таку вибірку не впливає: курсор указує на конкретний елемент, а не на позицію. Користувач гортає стрічку без дублікатів і пропусків — це і є консистентність даних при читанні.

Вартість запитів теж розходиться. При зсуві база відраховує і відкидає всі попередні рядки, тому глибоке прокручування дорожчає з кожною сторінкою: сота порція читає марно тисячі записів.

Курсор працює за індексом і бере рівно потрібний діапазон — час відповіді залишається рівним на будь-якій глибині стрічки.

Складність підтримки вища у курсора, і це чесна ціна. Потрібен стабільний ключ сортування, складений вказівник при рівних значеннях, окрема логіка для «назад» і для переходу до довільної позиції.

Зате сторінкова пагінація, дешева в реалізації, обростає костилями рівно там, де стрічка оновлюється часто. Для нескінченного прокручування вибір однозначний: курсор.

Технічні сумніви: утечі, дублі, повернення на екран

Infinite scroll майже завжди ламається не на першому екрані, а на поверненні: користувач іде в картку товару, повертається — і стрічка стрибає на початок.

Причина в тому, що список перестворюється заново, а стан підвантаження і позиція скролу живуть у пам'яті екрана, який система вивантажила при відході у фон.

Лікується це збереженням стану стрічки окремо від самого екрана: зміщення, завантажені сторінки і ознаку «дані ще є» кладемо в модель, що переживає перестворення, і при поверненні відновлюємо позицію до відрисовки, а не після.

Дублі записів — друга за частотою скарга, і майже завжди це гонка двох запитів. Користувач смикає стрічку вниз, спрацьовує підвантаження, він смикає ще раз — іде два запити за однією і тією ж сторінкою, і обидві відповіді дописуються в список.

Закривається це блокуванням на час активного запиту плюс дедуплікацією за стійким ідентифікатором запису, а не за індексом у масиві: індекс зсувається при вставці, ідентифікатор — ні.

Чому стрічка втрачає позицію після згортання застосунку?

Система вивантажує екран з пам'яті, щоб звільнити ресурси, і при поверненні будує його заново. Якщо позиція скролу і завантажені сторінки зберігалися тільки в самому екрані, вони губляться разом з ним.

Рішення — винести цей стан у шар, який живе довше за екран, і відновлювати позицію до першої відрисовки списку.

Як обмежити пам'ять при довгій сесії?

Тримати в пам'яті всю прокручену стрічку не можна — на довгій сесії це призводить до зростання споживання і падіння. Ми обмежуємо вікно: зберігаємо останні N екранів плюс запас, решту вивантажуємо, а при поверненні вгору підтягуємо заново. Користувач цього не помічає, а споживання пам'яті перестає зростати з довжиною сесії.

Що робити, якщо дані на сервері змінилися між завантаженнями?

Список, зібраний з різних запитів, може містити застарілі записи. Ми позначаємо сторінки часом завантаження і оновлюємо видиме вікно при поверненні на екран, а видалені записи прибираємо за ідентифікатором. Так стрічка залишається цілісною без повного перезавантаження.

Ці сценарії ми закриваємо на етапі проєктування стрічки, а не після скарг у підтримку — розбираємо, як це влаштовано у вашому застосунку.

Обговоримо задачу і повернемося з оцінкою та планом

Щоб запропонувати по суті, а не «за прайсом», нам потрібно зрозуміти вашу поточну стрічку: як вона влаштована зараз, де вже просідає, які сценарії для вас критичні. На першій розмові розбираємо не абстрактні побажання, а конкретику — що розгорнуто, чим міряється, що болить.

Після цього повертаємося з оцінкою робіт і планом впровадження: що робимо в першу чергу, що можна відкласти, де ризики. Нижче — що варто тримати під рукою до обговорення.

  • Як зараз влаштована підгрузка: сторінки, курсор, розмір порції — і що відбувається на повільній мережі.
  • Які сценарії критичні: стрічка товарів, чат, соцфід, пошук — у кожного свої вимоги до підгрузки.
  • Що відбувається при збої: порожній екран, дублі карток, втрата позиції при поверненні — які з цих симптомів уже бачили.
  • Чи є кеш і офлайн-режим, як поводиться застосунок при обриві зв'язку.
  • Як міряється якість: швидкість першої порції, частка довантажень, відмови на довгому прокручуванні.
  • Що вже пробували і чому не влаштувало — щоб не повторювати пройдене.
  • Обмеження за строками та релізним циклом: коли потрібен результат і що не можна ламати в поточній версії.

Опишіть задачу в двох словах — який застосунок, яка стрічка, що турбує. Повернемося з оцінкою та планом, без загальних слів.

Наші компетенції:

Останні роботи

  • Розробка мобільного додатка для компанії FEEDME
    Розробка мобільного додатка для компанії FEEDME
    1010
  • Розробка мобільного додатку для компанії XOOMER
    Розробка мобільного додатку для компанії XOOMER
    903
  • Розробка мобільного додатку для компанії RHL
    Розробка мобільного додатку для компанії RHL
    1332
  • Розробка мобільного додатку для компанії ZIPPY
    Розробка мобільного додатку для компанії ZIPPY
    1180
  • Розробка мобільного додатку для компанії Affhome
    Розробка мобільного додатку для компанії Affhome
    1107
  • Розробка мобільного додатку для компанії FLAVORS
    Розробка мобільного додатку для компанії FLAVORS
    714