Bridge هو مكون معماري لـ React Native يوفر اتصالاً غير متزامن بين خيط JavaScript والبيئة الأصلية لـ iOS و Android. ينقل رسائل JSON المسلسلة عبر طابور، مما يسمح باستدعاء APIs الأصلية من كود JS. وفقًا لـ Meta، 2024، يظل Bridge أساس التطبيقات القائمة، وإن كان يخسر في الأداء أمام المعمارية الجديدة على JSI.
النقاط الرئيسية
Bridge هو عنصر معماري رئيسي في React Native يوفر اتصالاً غير متزامن ثنائي الاتجاه بين خيط JavaScript، حيث تتنفذ منطق التطبيق، وخيوط iOS و Android الأصلية. منذ إصدار React Native في 2015، ظل Bridge الطريق الوحيد لتفاعل كود JS مع APIs المنصة — الكاميرا، الجيولوكاتيون، نظام الملفات، الإشعارات والقدرات الأصلية الأخرى.
تعتمد معمارية Bridge على مبدأ طابور الرسائل (message queue). عندما يستدعي كود JavaScript طريقة أصلية، يتم تسلسل الطلب إلى سلسلة JSON، ووضعه في طابور، وإرساله بشكل غير متزامن إلى الجانب الأصلي. يقوم الكود الأصلي بمعالجة الطلب، وتنفيذ العملية المناسبة، وإرسال النتيجة عبر نفس الطابور إلى خيط JS. وفقًا لتقرير Meta في React Conf 2021، يمر عبر Bridge ما يصل إلى 10 000 رسالة في الثانية في التطبيق المتوسط.
الخيوط الرئيسية المشاركة في عمل Bridge: JavaScript Thread (تنفيذ كود JS)، Native Thread (تنفيذ العمليات الأصلية) و Shadow Thread (حساب التخطيط باستخدام Yoga). يعمل كل خيط بشكل مستقل، مما يضمن استجابة واجهة المستخدم — لا يتم حجب الرسوم المتحركة الأصلية بواسطة حسابات JS.
يستخدم Bridge ثلاث آليات رئيسية للاتصال: MessageQueue، تسلسل JSON وتجميع الرسائل. MessageQueue هو مكون داخلي في React Native يدير طابور الاستدعاءات بين JS والجانب الأصلي. يتم وضع كل استدعاء لطريقة أصلية في طابور، ثم يتم تسلسله وإرساله في دفعات لتحسين الأداء.
يعمل MessageQueue على مبدأ الدفعات: تتراكم استدعاءات الطرق الأصلية وترسل كمجموعة واحدة كل 5–15 ملي ثانية. يقلل هذا من عبء التسلسل، حيث يتم تعبئة عدة استدعاءات في حزمة JSON واحدة. في الجانب الأصلي، يتم إلغاء تسلسل الرسائل وتوزيعها على الوحدات المناسبة.
تتم تسجيل الوحدات الأصلية تلقائياً عبر الماكرو أو التعليقات. يستخدم iOS الماكرو RCT_EXPORT_MODULE، ويستخدم Android التعليق @ReactMethod. يقوم React Native بفحص الوحدات المسجلة عند بدء التطبيق ويبني خارطة JSON تكوينية لجميع الطرق المتاحة. تُمرّر هذه الخارطة إلى بيئة JS، ويتعلم JavaScript الطرق التي يمكن استدعاؤها.
تتبع البيانات هذا المسار: JavaScript يستدعي NativeModules.CalendarModule.createCalendarEvent(). يتم تسلسل الطريقة إلى رسالة JSON بمعرف الوحدة واسم الطريقة والوسائط. تدخل الرسالة إلى MessageQueue. في الخيط الأصلي، يتم إلغاء تسلسل الرسالة وتمريرها إلى الوحدة المناسبة. يتم تسلسل نتيجة التنفيذ عائداً وإرسالها إلى خيط JS كـ Promise أو callback.
// استدعاء الوحدة النمطية الأصلية من JavaScript عبر Bridge
import { NativeModules } from 'react-native';
const CalendarModule = NativeModules.CalendarModule;
CalendarModule.createCalendarEvent('Test Event', 'Office')
.then(eventId => {
console.log('تم إنشاء حدث بالمعرف:', eventId);
})
.catch(error => {
console.error('فشل:', error);
});
في الجانب الأصلي لـ iOS، تبدو الوحدة كفصل Objective-C مع الماكرو RCT_EXPORT_MODULE. يتم تصدير الطريقة باستخدام الماكرو RCT_EXPORT_METHOD، ويقوم React Native بتسجيلها تلقائياً في Bridge. تُمرر الوسائط حسب الموقع ويجب أن تتوافق مع أنواع JSON المدعومة: NSString، NSNumber، NSArray، NSDictionary، BOOL.
// تسجيل الوحدة النمطية الأصلية لنظام iOS في 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، مما يضيف زمن استجابة ويستهلك الذاكرة. بالنسبة للعمليات ذات البيانات الكبيرة، مثل معالجة الصور أو العمل مع الفيديو، يصبح هذا عنق زجاجة.
تستهلك عمليتي التسلسل وإلغاء التسلسل وقت وحدة المعالجة المركزية والذاكرة. يجب تحويل كل رسالة إلى سلسلة في جانب JS، نقلها عبر الجسر وتحليلها في الجانب الأصلي. وفقًا لاختبارات Callstack (2022)، يستغرق تسلسل مصفوفة من 10 000 رقم عبر Bridge حوالي 30–50 ملي ثانية، وهو أمر غير مقبول للاستدعاءات عالية التردد.
Bridge غير محسّن لنقل البيانات الثنائية الكبيرة. تتطلب الصور وملفات الصوت وتدفقات الفيديو أساليب بديلة — على سبيل المثال، كتابة ملف على القرص وتمرير المسار كسلسلة. يخلق هذا عبءاً إضافياً على عمليات القراءة والكتابة لنظام الملفات.
أدى إدراك هذه القيود إلى تطوير فريق Meta لمعمارية React Native جديدة، حيث يتم استبدال Bridge بـ JSI (واجهة JavaScript) و 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 الذي يتم تحميله بشكل بطيء ويدعم إعادة التحميل الفورية للوحدات دون إعادة التشغيل.
الأسئلة الشائعة
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. سوف نقدم لك النصح ونقترح أفضل حل.
اقرأ أيضًا