ما وراء الجسر: هندسة وحدات برمجية أصلية (Native Modules) عالية الأداء باستخدام Nitro و JSI
أتعمق في كيفية قيام Nitro و JSI بالتخلص من مشكلة الـ JSON-serialization في React Native. اكتشف كيفية بناء وحدات برمجية أصلية متزامنة (synchronous) وآمنة الأنواع (type-safe) تعمل بسرعة C++.

لسنوات، كان بناء الـ native modules في React Native يشبه رمي البيانات فوق جدار. كنا نأخذ كائنات JavaScript النظيفة، ونقوم بتحويلها (serialize) إلى نصوص JSON، ثم نرسلها عبر طابور (queue)، ونأمل أن تصل بأمان إلى الطرف الآخر. هذا هو "الجسر" (Bridge) كما نعرفه، وبصراحة، لقد كان العائق الرئيسي لكل تطبيق عالي الأداء قمت بهندسته على الإطلاق.
اليوم، أريد التحدث عن التحول نحو معمارية Zero-JS Bridge. من خلال الاستفادة من JavaScript Interface (JSI) وإطار عمل Nitro Modules الناشئ، ننتقل إلى عصر يمكن فيه لـ JavaScript و C++ وأخيراً مشاركة الذاكرة مباشرة. لا serialization، لا queues، فقط أداء عالٍ.
المشكلة: ضريبة الـ Serialization
في المعمارية القديمة، في كل مرة تستدعي فيها وظيفة native، يحدث ما يلي:
- يقوم الـ JS thread بـ serialize للمدخلات إلى JSON.
- يتم تمرير الرسالة إلى Bridge queue.
- يقوم الـ Native thread باستلام الرسالة وعمل deserialize لها.
- يتم حساب النتيجة، ثم عمل serialize لها مرة أخرى إلى JSON.
- يستلم الـ JS thread الرد ويقوم بعمل deserialize له.
عندما تحاول معالجة إطارات الكاميرا بمعدل 60 إطاراً في الثانية أو مزامنة حركات (animations) معقدة، تكون هذه "الضريبة" قاتلة. أنت تفقد أجزاءً ثمينة من الثانية في مجرد نقل البيانات.
هنا يأتي دور JSI (JavaScript Interface)
الـ JSI عبارة عن طبقة C++ خفيفة تسمح لنا بكشف الـ native methods مباشرة لمحرك JavaScript (Hermes أو V8). بدلاً من نظام تمرير الرسائل، يتيح لنا JSI الاحتفاظ بمراجع (references) لكائنات native داخل JavaScript.
لقد وجدت أن التعامل مع JSI الخام قوي جداً ولكنه مطول للغاية. كتابة روابط C++ يدوياً لكل خاصية ودالة أمر معرض للأخطاء ومرهق. هنا يأتي دور Nitro ليغير قواعد اللعبة.
تطور Nitro
تعتبر Nitro Modules (التي أنشأها Marc Rousavy) التطور التالي لهذا المفهوم. فهي تعمل كغلاف (wrapper) عالي الأداء وآمن الأنواع (type-safe) حول JSI. تعتمد على نهج توليد الكود (code-generation) الذي يسمح لنا بتعريف واجهاتنا (interfaces) في TypeScript، وتتولى هي المهمة الثقيلة لتوليد الروابط (bindings) الخاصة بـ C++ أو Swift أو Kotlin.
إليكم كيف أقوم حالياً بهيكلة هذه الوحدات. أولاً، نقوم بتعريف العقد (contract):
يقوم Nitro بعد ذلك بتوليد الـ boilerplate. في الجانب الأصيل (Swift/C++)، يمكنني تنفيذ هذا المنطق بشكل متزامن (synchronously). ولأن Nitro يستخدم رسم خرائط الذاكرة المباشر (direct memory mapping) عبر JSI، فعندما أقوم باستدعاء calculator.multiply(5, 5)، يكون هناك صفر تأخير (zero overhead). محرك JavaScript يحمل حرفياً مؤشراً (pointer) إلى دالة C++.
لماذا يهم هذا في الهندسة
في مشاريعي الأخيرة، أدى الانتقال إلى معمارية Nitro/JSI إلى تحقيق ثلاثة إنجازات رئيسية:
- التنفيذ المتزامن (Synchronous Execution): لم نعد مضطرين لاستخدام
awaitلكل استدعاء native. إذا كان الحساب يحتاج إلى الحدوث فوراً لمنع حدوث خلل في واجهة المستخدم (UI glitch)، فإنه يحدث في نفس اللحظة. - الذاكرة المشتركة (Shared Memory): بالنسبة لمجموعات البيانات الكبيرة (مثل buffers الصور أو هياكل JSON الضخمة)، يمكننا استخدام
ArrayBuffersومشاركتها بين JS و C++ دون نسخ البيانات. هذا يقلل من ضغط الـ garbage collector بشكل كبير. - أمان الأنواع عبر الحدود (Type Safety across the Boundary): يضمن توليد الكود (codegen) أنه إذا كانت TypeScript تقول أن الدالة تعيد
string، فيجب أن يتطابق تنفيذ C++ مع ذلك. لا مزيد من انهيارات التطبيق أثناء التشغيل بسبب قيمnullغير المتوقعة من الـ bridge.
استراتيجية التنفيذ
إذا كنت تتطلع لتنفيذ هذا في مشروعك، فلا تحاول نقل كل شيء مرة واحدة. حدد المسارات الحرجة (hot paths). من خبرتي، عادة ما تكون هذه:
- منطق معالجة الصور والفيديو.
- طبقات قواعد البيانات (مثل روابط SQLite).
- التحويلات الرياضية المعقدة للإيماءات (gestures).
بمجرد نقل هذه المسارات إلى وحدة JSI مدعومة بـ Nitro، يظهر فرق الأداء فوراً. لقد انتقلنا من 12ms تأخير لكل استدعاء bridge إلى 0.01ms فعلياً. هذا هو الفرق بين تطبيق "جيد" وتطبيق "يعطي شعوراً بالأصالة" (native-feeling).
كلمات أخيرة
الـ bridge يحتضر، وبصراحة، يجب أن نحتفل بذلك. كمهندسي React Native، هدفنا هو جعل التجريد (abstraction) نحيفاً قدر الإمكان. Nitro و JSI هما أحدة الأدوات التي نملكها لهذه المهمة. يتطلب الأمر التعود على القليل من C++ والروابط الأصلية (native glue)، لكن مكاسب الأداء غير قابلة للتفاوض في الهندسة رفيعة المستوى.