РозробкаБудь-який рівень

React Native

React Native — це React, який рендерить справжні в’ю платформи замість вузлів DOM. Це не вебв’ю і не компілятор у Swift чи Kotlin: ваш JavaScript виконується у власному рушії й каже платформі, що показати на екрані. Саме ця архітектура дозволяє вебкоманді випустити справжній мобільний застосунок — і саме вона породжує всі питання продуктивності до фреймворку. На цій сторінці — модель потоків, Expo, нативні модулі й те, де ілюзія ламається.

В’ю
Справжні в’ю платформи
Ваш код
Один потік JS
Спільно
Логіка, не весь інтерфейс

Що насправді на екрані

<View> у React Native — це не div і не малюнок. Він стає справжнім UIView на iOS і справжнім android.view.View на Android, який створює й малює платформа. Тому скрол має правильну фізику, клавіатура поводиться коректно, а застосунок безкоштовно успадковує доступність: нічого з цього не переписано — це операційна система робить свою роботу.

Ваш React-код виконується в одному потоці JavaScript. В’ю, яких насправді торкається користувач, — це справжні нативні в’ю в головному потоці платформи, а розкладку міряють у третьому. Усе, що одна сторона потребує від іншої, має перетнути межу між ними: старий міст серіалізував ці повідомлення й пакетував їх, нова архітектура викликає навпростець через JSI. Усе, кероване з боку JavaScript, зупиняється, коли той потік зайнятий, — тому жест, анімований нативно, лишається плавним, а анімований із JavaScript смикається.

Ваш JavaScript тим часом виконується в окремому рушії — тепер типово це Hermes — у власному потоці. Він ніколи не торкається цих в’ю напряму. Він описує, який вигляд має мати дерево; цей опис перетинає межу на нативний бік, третій потік міряє розкладку, а головний потік застосовує результат. Раз подивитися на схему цього вартує більше за будь-який список порад з оптимізації, бо кожна проблема продуктивності в React Native — це питання про той перехід.

Початковий задум серіалізував кожне повідомлення через цю межу як JSON і надсилав пакетами — це й був «міст». Він працював і породив фірмовий збій фреймворку: коли потік JavaScript зайнятий, нативний бік отримує вказівки пізно, і кадри втрачаються. Нова архітектура це замінює. JSI дозволяє JavaScript тримати пряме посилання на нативний об’єкт і викликати його синхронно, Fabric перебудовує на цьому рендеринг, а TurboModules завантажують нативні модулі ліниво, а не всі на старті.

Expo і чому починати варто з нього

Expo колись був пісочницею, яку зрештою доводилося покидати. Тепер це не так, і порада справді змінилася: починайте новий проєкт на React Native з Expo, якщо немає конкретної причини цього не робити. Він дає керований сервіс збірки, оновлення «по повітрю», великий набір підтримуваних нативних модулів і — найважливіше — config plugins, які описують зміни в нативному проєкті кодом, а не правками руками, що губляться при наступному оновленні.

Саме цей пункт вирішує, як проєкт старітиме. «Голий» React Native-застосунок містить теки ios та android, які ви правите напряму, і кожне оновлення фреймворку перетворюється на злиття зі зміненими вами файлами. З config plugins ці теки генеруються, тож оновлення їх перегенерує, а ваші налаштування накотяться заново. Команди, що правлять нативні проєкти руками, — це ті самі, що через два роки досі сидять на три мажорні версії позаду.

Скільки насправді спільного

Обіцянка, яку чують, — одна кодова база на дві платформи. Чесна цифра така: бізнес-логіка, мережа, стан і більшість екранів спільні, а решта — ні. Патерни навігації різні, жест «назад» є на одній платформі й немає на іншій, діалоги дозволів поводяться інакше, і кожен магазин має власні правила щодо того, що ви зобов’язані показати. React Native дає Platform.select і файли .ios.tsx / .android.tsx саме тому, що останні десять відсотків мають розходитися.

АспектСпільне?Що це означає на практиці
Бізнес-логіка, API, станТак, повністюОсновна маса того, що ви пишете, — і причина, чому підхід узагалі окупається.
Екрани й компонентиЗдебільшогоТой самий код із гілками платформи для відступів, заголовків і поведінки «назад».
Відчуття навігаціїЧастковоБібліотека це абстрагує; користувачі все одно чекають конвенцій своєї платформи.
Дозволи, пуші, диплінкиНіНалаштовуються окремо, і кожен магазин перевіряє їх за своїми правилами.
Реліз і рев’юНіДва пайплайни, два набори сертифікатів, дві черги рев’ю — назавжди.

Коли для можливості немає JavaScript-API, ви пишете нативний модуль: невеликий шматок Swift чи Kotlin, відкритий вашому JavaScript. Це нормально й не є провалом підходу, але це момент, коли команді потрібен хтось, здатний читати документацію платформи. Проєкт на React Native, де ніхто не розуміє нативного боку, зрештою впреться в задачу, до якої не дотягнеться жоден обсяг JavaScript: фонове вивантаження, віджет, платіжний SDK, що постачається лише нативно.

Як це виявляється в реальній поставці

Повторювана скарга на продуктивність — це списки. Довгий список, відрендерений через map, будує кожен рядок незалежно від того, чи він на екрані, а на середньому Android-телефоні це різниця між застосунком, що відчувається нативним, і тим, що ні. Беріть віртуалізований список, дайте йому стабільний keyExtractor, мемоїзуйте компонент рядка й тримайте висоти рядків передбачуваними. Майже кожна заява «React Native повільний» зводиться до списку, зайвого перерендеру або зображення, якому ніколи не змінювали розмір.

Друге, що варто планувати заздалегідь, — оновлення. React Native рухається швидко, і екосистема навколо рухається разом із ним, тож проєкт, який рік пропускає версії, стикається з оновленням, де фреймворк, бібліотека навігації, нативні модулі й SDK платформ їдуть одночасно. Оновлюватися по одній мінорній версії — нудно й займає пів дня; оновитися одразу на чотири — займає два тижні й ламає речі, причину яких ніхто не встановить.

Де це вироджується

  • Анімації, керовані з JavaScript, смикаються тієї миті, коли потік має справжню роботу.
  • Довгі списки через map замість віртуалізованого списку рендерять рядки, яких ніхто не побачить.
  • Правлені руками теки ios та android перетворюють кожне оновлення фреймворку на конфлікт злиття.
  • Одна розкладка на обидві платформи без огляду на їхні конвенції навігації.
  • Команда взагалі без нативних навичок стає першого ж разу, коли фічі потрібен модуль.
  • Тестування лише на свіжому iPhone, де потік JS достатньо швидкий, щоб приховати всі помилки вище.
  • Розрахунок на OTA-оновлення для змін, що зачіпають нативний код: так їх не доставити.

Коли це застосовувати

Застосовуйте, коли

  • Продукт, якому потрібні обидві платформи, а команда вже пише React і TypeScript.
  • Контентні, комерційні та робочі застосунки — форми, списки, навігація, мережа, — тобто більшість того, що будують.
  • Продукти, яким вигідно доставляти виправлення без черги рев’ю — у межах того, що може нести OTA.
  • Команди, яким потрібні нативні в’ю та нативна доступність, а не їх відтворення.

Уникайте, коли

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

Було корисно?

Поділіться з тим, хто працює над тією ж задачею.