«Приложение тормозит» это не диагноз. На собесе ждут, что вы опишете процесс: воспроизвести, измерить, найти поток-виновника, исправить, измерить снова. И знаете инструменты.
Инструменты отладки
- React Native DevTools: стандартный отладчик для Hermes (появился в 0.76). Консоль, точки останова, вкладки React Components и Profiler. Открывается из меню разработчика или клавишей
jв Metro. - Flipper больше не поддерживается для RN: его убрали из шаблона проекта, а его задачи закрывают DevTools, плагины Expo и нативные инструменты.
- Нативные инструменты: Android Studio Profiler (CPU, память, сеть), Xcode Instruments (Time Profiler, Allocations, Leaks). Нужны, когда проблема на нативной стороне.
- Perf Monitor в меню разработчика показывает частоту кадров JS и UI потоков.
- Логи нативной стороны:
adb logcat, консоль Xcode. Падения нативного кода в JS-консоли не видно.
Debug не мерить
В debug-сборке JS работает медленнее, включены проверки и предупреждения, React в режиме разработки. Производительность измеряют на release-сборке (или профилируемой) на реальном слабом устройстве, а не на флагманском телефоне и не в симуляторе.
Какой поток тормозит
- JS-поток: долгие рендеры, тяжёлые вычисления, разбор больших JSON, частые перерисовки. Симптом: кнопки отвечают с задержкой, анимации на JS дёргаются, но нативная прокрутка ещё плавная.
- UI-поток: тяжёлая раскладка, большие изображения, сложные тени и прозрачность, огромное количество представлений. Симптом: дёргается всё, включая нативную прокрутку.
Лишние рендеры
Профайлер React показывает, какие компоненты рендерились и почему. Типичные причины:
- новые объекты и функции в пропсах на каждый рендер (инлайновые стили, стрелки) ломают
memo; - Context с часто меняющимся значением перерисовывает всех потребителей;
- состояние слишком высоко в дереве;
- подписка на весь стор вместо селектора.
Лечение: memo, useCallback, useMemo там, где профайлер показал проблему, селекторы, разделение контекстов, перенос состояния вниз. React Compiler умеет мемоизировать автоматически, но понимать причины всё равно нужно.
Тяжёлая работа
- Отложить до окончания анимации перехода: исторически через
InteractionManager.runAfterInteractions, сейчас чаще черезrequestIdleCallbackили явный запуск после событияtransitionEndнавигатора. - Разбить на части или перенести в натив.
- Для сложных вычислений есть библиотеки потоков и worklets на отдельном рантайме.
Время запуска
Измеряют время до первого полезного экрана (TTI). Что помогает:
- Hermes с байткодом (по умолчанию);
- ленивые импорты тяжёлых экранов и модулей;
- меньше работы при старте: не грузить всё подряд, отложить аналитику;
- ленивые TurboModules в новой архитектуре;
- уменьшение бандла: убрать неиспользуемые зависимости, следить за размером.
Утечки памяти
Причины: незакрытые подписки (addEventListener без remove), таймеры без clearInterval, слушатели нативных событий, замыкания в глобальных кешах, большие изображения. Ищут снимками кучи в DevTools (вкладка Memory для JS) и нативными профайлерами для нативной памяти: открыть и закрыть экран несколько раз, сравнить снимки.
Сеть
Прокси вроде Charles и Proxyman или сетевые инструменты Expo и нативных профайлеров. Проверяют количество и размер запросов, кеширование, водопады запросов при открытии экрана.
Частые ошибки
- Мерить в debug на эмуляторе.
- Оптимизировать вслепую, без профайлера.
console.logв горячих местах: в release их убирают плагином Babel.- Искать нативную утечку в JS-профайлере.
Что спросят на собесе
- Какие инструменты отладки вы используете сейчас.
- Как понять, какой поток тормозит.
- Как найти и убрать лишние рендеры.
- Как ускорить запуск приложения.
- Как искать утечки памяти.