Bridge — это архитектурный компонент React Native, обеспечивающий асинхронную коммуникацию между JavaScript-потоком и нативной средой iOS и Android. Он передаёт сериализованные JSON-сообщения через очередь, позволяя вызывать нативные API из JS-кода. По данным Meta, 2024, Bridge остаётся основой существующих приложений, хотя уступает по производительности новой архитектуре на JSI.
Главное
Bridge (Мост) — это ключевой архитектурный элемент React Native, который обеспечивает двустороннюю асинхронную связь между JavaScript-потоком, где выполняется бизнес-логика приложения, и нативными потоками iOS и Android. С момента выхода React Native в 2015 году Bridge оставался единственным способом взаимодействия JS-кода с платформенными API — камера, геолокация, файловая система, уведомления и другие нативные возможности.
Архитектура Bridge основана на принципе очередности сообщений (message queue). Когда JavaScript-код вызывает нативный метод, запрос сериализуется в JSON-строку, помещается в очередь и асинхронно отправляется на нативную сторону. Нативный код обрабатывает запрос, выполняет соответствующую операцию и отправляет результат обратно через ту же очередь в JS-поток. По данным доклада Meta на React Conf 2021, через Bridge проходит до 10 000 сообщений в секунду в среднестатистическом приложении.
Основные потоки, участвующие в работе Bridge: JavaScript Thread (выполнение JS-кода), Native Thread (выполнение нативных операций) и Shadow Thread (расчёт макета с помощью Yoga). Каждый поток работает независимо, что обеспечивает отзывчивость UI — нативные анимации не блокируются JS-вычислениями.
Bridge использует три ключевых механизма для коммуникации: MessageQueue, сериализацию JSON и батчинг сообщений. MessageQueue — это внутренний компонент React Native, который управляет очередью вызовов между JS и нативной стороной. Каждый вызов нативного метода помещается в очередь, сериализуется и отправляется батчами для оптимизации производительности.
MessageQueue работает по принципу батчинга: вызовы нативных методов накапливаются и отправляются одной группой (batch) через каждые 5–15 миллисекунд. Это снижает накладные расходы на сериализацию, так как несколько вызовов упаковываются в один JSON-пакет. На нативной стороне сообщения десериализуются и распределяются по соответствующим модулям.
Нативные модули регистрируются автоматически через макросы или аннотации. На iOS используется макрос RCT_EXPORT_MODULE, на Android — аннотация @ReactMethod. React Native сканирует зарегистрированные модули при запуске приложения и строит конфигурационную JSON-карту всех доступных методов. Эта карта передаётся в JS-среду, и JavaScript узнаёт, какие методы можно вызывать.
Данные проходят следующий путь: JavaScript вызывает NativeModules.CalendarModule.createCalendarEvent(). Метод сериализуется в JSON-сообщение с идентификатором модуля, именем метода и аргументами. Сообщение попадает в очередь MessageQueue. На нативном потоке сообщение десериализуется и передаётся в соответствующий модуль. Результат выполнения сериализуется обратно и отправляется в JS-поток как Promise или callback.
// Native module call from JavaScript via Bridge
import { NativeModules } from 'react-native';
const CalendarModule = NativeModules.CalendarModule;
CalendarModule.createCalendarEvent('Test Event', 'Office')
.then(eventId => {
console.log('Created event with id:', eventId);
})
.catch(error => {
console.error('Failed:', error);
});
На нативной стороне iOS модуль выглядит как Objective-C класс с макросом RCT_EXPORT_MODULE. Метод экспортируется макросом RCT_EXPORT_METHOD, и React Native автоматически регистрирует его в Bridge. Аргументы передаются по позиции и должны соответствовать поддерживаемым типам JSON: NSString, NSNumber, NSArray, NSDictionary, BOOL.
// iOS Native Module registration in Bridge
@interface CalendarModule () RCT_EXPORT_MODULE()
@end
@implementation CalendarModule
RCT_EXPORT_METHOD(createCalendarEvent:(NSString *)name
location:(NSString *)location
resolver:(RCTPromiseResolveBlock)resolve
rejecter:(RCTPromiseRejectBlock)reject)
{
NSNumber *eventId = createEvent(name, location);
resolve(eventId);
}
@end
Bridge имеет ряд фундаментальных ограничений производительности. Главное из них — обязательная асинхронность и сериализация. Каждый вызов нативного метода превращает данные в JSON-строку, что добавляет задержку и потребляет память. Для операций с большими объёмами данных, таких как обработка изображений или работа с видео, это становится узким местом.
Сериализация и десериализация JSON занимают процессорное время и память. Каждое сообщение должно быть преобразовано в строку на JS-стороне, передано через мост и распарсено на нативной стороне. По данным тестов Callstack (2022), сериализация массива из 10 000 чисел через Bridge занимает около 30–50 миллисекунд, что неприемлемо для high-frequency вызовов.
Bridge не оптимизирован для передачи больших бинарных данных. Фотографии, аудиофайлы и video-потоки требуют альтернативных подходов — например, записи файла на диск и передачи пути в виде строки. Это создаёт дополнительные накладные расходы на чтение и запись файловой системы.
Осознание этих ограничений привело команду Meta к разработке новой архитектуры React Native, в которой Bridge заменяется на JSI (JavaScript Interface) и Turbo Module. JSI позволяет вызывать нативные методы напрямую, без сериализации, что устраняет главный недостаток Bridge.
Сравнение Bridge и Turbo Module показывает фундаментальные различия в архитектурных подходах. Bridge использует асинхронную очередь сообщений с JSON-сериализацией, тогда как Turbo Module работает через JSI — прямой интерфейс между JavaScript и C++, который позволяет синхронно вызывать нативные методы без преобразования данных.
| Характеристика | Bridge | Turbo Module |
|---|---|---|
| Тип вызова | Асинхронный | Синхронный и асинхронный |
| Сериализация | JSON на каждый вызов | JSI-объекты без копирования |
| Производительность | Средняя | Высокая |
| Типизация | Динамическая | Статическая (Codegen) |
| Загрузка | Все модули при старте | Ленивая (по требованию) |
Выбор между Bridge и Turbo Module зависит от версии React Native. Для проектов на React Native 0.72 и старше Bridge остаётся основным механизмом. Начиная с React Native 0.73, Metro и новая архитектура поддерживаются параллельно, позволяя постепенную миграцию. Полный переход на Turbo Module требует обновления до React Native 0.76+ и включения новой архитектуры в конфигурации.
Рассмотрим полный цикл создания и использования Native Module через Bridge на примере модуля для работы с календарём. Модуль будет создавать событие и возвращать его идентификатор. Этот пример охватывает настройку для обеих платформ — iOS и Android.
На Android Native Module создаётся как Java-класс, наследующий ReactContextBaseJavaModule. Аннотация @ReactMethod экспортирует метод в Bridge. Для Promise используется интерфейс Promise из com.facebook.react.bridge.
public class CalendarModule extends ReactContextBaseJavaModule {
@Override
public String getName() {
return "CalendarModule";
}
@ReactMethod
public void createCalendarEvent(
String name,
String location,
Promise promise) {
try {
Integer eventId = createCalendarEventNative(name, location);
promise.resolve(eventId);
} catch (Exception e) {
promise.reject("EVENT_ERROR", e.getMessage());
}
}
}
Модуль регистрируется через @ReactModule или вручную в пакете приложения. React Native автоматически обнаруживает и добавляет его в Bridge. После регистрации модуль доступен из JavaScript через NativeModules.
public class CalendarPackage implements ReactPackage {
@Override
public List<NativeModule> createNativeModules(
ReactApplicationContext reactContext) {
return Arrays.asList(
new CalendarModule(reactContext)
);
}
@Override
public List<ViewManager> createViewManagers(
ReactApplicationContext reactContext) {
return Collections.emptyList();
}
}
Важно отметить, что Bridge требует перезапуска приложения при добавлении новых модулей, так как конфигурационная карта строится один раз при инициализации. Это отличает его от Turbo Module, который загружается лениво и поддерживает hot-reload модулей без перезапуска.
Часто задаваемые вопросы
Bridge всегда использует асинхронную очередь и JSON-сериализацию, тогда как прямая передача через JSI работает синхронно и без копирования данных. Bridge создаёт задержку на сериализацию, но обеспечивает изоляцию потоков.
Нет, Bridge поддерживает только асинхронные вызовы. Для синхронного взаимодействия требуется новая архитектура с JSI и Turbo Module. Это одно из ключевых ограничений, которое решено в React Native 0.76+.
Bridge поддерживает типы, сериализуемые в JSON: строки, числа, булевы значения, массивы, словари (объекты). Бинарные данные, такие как изображения, необходимо передавать через файловую систему или base64-кодирование.
Для измерения используйте React DevTools и профилировщик React Native. Вкладка Performance показывает количество сообщений в очереди Bridge и задержки. Также доступен пакет react-native-bridge-spy для мониторинга трафика.
Рекомендуется переходить при проектах, требовательных к производительности, или при создании новых приложений на React Native 0.76+. Для существующих проектов миграция может быть постепенной — обе архитектуры работают параллельно.
Итоги
Мы разработаем мобильное приложение под ключ
IT Sectr создаёт приложения для iOS и Android для стартапов и бизнеса с 2017 года. Мы проконсультируем вас и предложим наилучшее решение.
Читайте также