JSI (JavaScript Interface) هي طبقة برمجية في React Native توفر وصولاً متزامناً مباشراً من JavaScript إلى كائنات ودوال C++، لتحل محل جسر JSON غير المتزامن Bridge. على عكس سابقه، يسمح JSI باستدعاء الطرق الأصلية دون تسلسل الرسائل وتمرير المراجع إلى كائنات C++ مباشرة إلى بيئة JS. وفقاً لـ فريق React Native (2025)، يوفر JSI تسارعاً يصل إلى 10 أضعاف في التفاعل بين JS والكود الأصلي في السيناريوهات كثيفة تبادل البيانات.
النقاط الرئيسية
JSI (JavaScript Interface) هي طبقة C++ تزود محرك JavaScript (Hermes، JavaScriptCore، V8) بالقدرة على الوصول المباشر إلى كائنات ودوال وذاكرة C++. على عكس Bridge، الذي كان يسلسل الاستدعاءات إلى JSON ويمررها عبر قائمة انتظار غير متزامنة، يتيح JSI لكود JS استدعاء طرق C++ بشكل متزامن والحصول على النتيجة فوراً.
تم تقديم JSI في React Native 0.64 كجزء من البنية الجديدة. كان الهدف الرئيسي هو إزالة عنق الزجاجة الذي مثله Bridge: كل تفاعل بين JS والكود الأصلي كان يستهلك وقتاً في التسلسل وإلغاء التسلسل والمرور عبر قائمة انتظار الرسائل. JSI يحل هذه المشكلة بإعطاء محرك JS وصولاً مباشراً إلى كائنات C++ من خلال أغلفة تنفذ واجهات jsi::Value و jsi::Object و jsi::Function.
JSI ليس بديلاً واحداً لواحد عن Bridge — إنه نهج مختلف جوهرياً للتكامل. كان Bridge يعمل مثل صندوق البريد: يرسل JS رسالة، تمر عبر قائمة انتظار، يعالجها الجانب الأصلي ويرسل رداً. يعمل JSI مثل المؤشر: يحصل JS على مرجع لكائن C++ ويمكنه استدعاء طرقه بشكل متزامن، مثل دوال JS العادية. هذا فرق جوهري في بنية التفاعل بين بيئتين.
نشأت الحاجة إلى JSI بسبب قيود Bridge الأصلي، الذي أُنشئ في React Native 2015. مع ازدياد شعبية الإطار وزيادة تعقيد التطبيقات، أصبحت مشكلة الأداء واضحة: كل استدعاء لوحدة أصلية كان يتطلب 3–5 مللي ثانية على الأقل للتسلسل. للعمليات البسيطة مثل قراءة قيمة مستشعر أو الحصول على حجم الشاشة، كان هذا مقبولاً، ولكن للرسوم المتحركة والعمل مع الرسوميات ومعالجة البيانات المتدفقة — كان أمراً بالغ الأهمية. بدأ فريق React Native العمل على البنية الجديدة في 2019، وأصبح JSI أساسها.
JSI مصممة كطبقة تجريد فوق محركات JavaScript. توفر API C++ موحدة يتم تنفيذها لكل محرك محدد: Hermes و JavaScriptCore (iOS) و V8 (Android). هذا يعني أن المطورين لا يحتاجون للقلق بشأن الاختلافات بين المحركات — Fabric و TurboModules يعملان بنفس الطريقة بغض النظر عن محرك JS المستخدم تحت الغطاء.
في قلب JSI يكمن مفهوم الكائنات المضيفة (Host Objects) — كائنات C++ يتم تصديرها إلى بيئة JS ككائنات JS أصلية. عندما يصل كود JS إلى خاصية أو طريقة لكائن من هذا القبيل، يعترض JSI الاستدعاء ويفوضه إلى طريقة C++ المقابلة. يحدث هذا بشكل متزامن، في نفس الخيط، دون تبديل السياق ودون تخصيص ذاكرة لسلسلة JSON.
كل كائن مضيف ينفذ واجهة jsi::HostObject بطرق get و set و getPropertyNames. محرك JS يستدعي هذه الطرق في كل مرة يتم الوصول فيها إلى خاصية الكائن. على سبيل المثال، عند استدعاء NativeModule.someMethod() في JS، يحول JSI هذا الاستدعاء إلى استدعاء C++ لطريقة كائن المضيف المقابلة. تمرر القيمة المعادة إلى JS كـ jsi::Value — نوع عام يمكن أن يمثل رقماً أو سلسلة أو قيمة منطقية أو كائناً أو undefined.
ميزة مهمة لـ JSI هي غياب قائمة انتظار الرسائل. كان Bridge يستخدم قائمة انتظار غير متزامنة: يرسل JS طلباً، يتحول إلى مهام أخرى، يعالج الجانب الأصلي الطلب، ويعود النتيجة عبر رد اتصال. يعمل JSI بشكل متزامن: إذا استدعى JS طريقة وحدة أصلية عبر JSI، يتوقف تنفيذ كود JS حتى يتم استلام النتيجة. هذا يبسط المنطق (لا حاجة لانتظار ردود الاتصال) ويزيل سباقات التوقيت (race conditions)، لكنه يتطلب الحذر — الاستدعاءات المتزامنة الطويلة تحظر خيط JS.
القيم التي تم إنشاؤها عبر JSI تعيش في بيئة تشغيل محرك JS وتدار بواسطة جامع القمامة. عندما ينشئ كود C++ jsi::String أو jsi::Object ويعيده إلى JS، تدير البيئة الذاكرة تلقائياً. إذا أراد كود C++ الاحتفاظ بمرجع لقيمة JS بين الاستدعاءات، يُستخدم jsi::Value::getWeak() أو jsi::Object::setProperty عالمي مع مرجع مخزن في كائن جذر بيئة التشغيل. هذا يمنع الإزالة المبكرة بواسطة جامع القمامة.
JSI ليست آمنة للخيوط افتراضياً. يجب أن تحدث جميع استدعاءات طرق JSI من الخيط الذي ينفذ فيه JS (عادةً خيط JS في React Native). إذا بدأت وحدة أصلية عملاً في الخلفية في خيط منفصل، يجب إعادة النتيجة عبر خيط JS باستخدام runOnJS من TurboModules. هذا القيد هو ثمن التزامن وغياب التسلسل.
الفرق بين JSI و Bridge جوهري ويؤثر على جميع جوانب التفاعل بين JS والكود الأصلي. كان Bridge غير متزامن، يسلسل البيانات إلى JSON ويستخدم قائمة انتظار رسائل؛ JSI متزامن، يعمل مع المراجع الأصلية ولا يتطلب تسلسلاً.
| المعلمة | Bridge | JSI |
|---|---|---|
| نموذج الاستدعاء | قائمة انتظار غير متزامنة | استدعاء مباشر متزامن |
| التسلسل | JSON (تسلسل + إلغاء تسلسل) | لا يوجد (مراجع مباشرة لكائنات C++) |
| زمن الانتظار | 3–10 مللي ثانية لكل استدعاء | 0.1–0.5 مللي ثانية لكل استدعاء |
| الكتابة | ديناميكية (عبر JSON) | ثابتة (عبر Codegen) |
| تكامل C++ | فقط عبر وحدات أصلية (Java/ObjC) | مباشر، بدون وسطاء |
| الخيط | خيط أصلي منفصل | خيط JS (متزامن) |
وفقاً لـ فريق React Native، أدى الانتقال من Bridge إلى JSI في تطبيق Facebook Marketplace إلى تقليل وقت بدء التشغيل بنسبة 35% وانخفاض استهلاك الذاكرة بنسبة 20% من خلال إلغاء ازدواجية البيانات بين جانبي JS والأصلي.
لم يكن Bridge “خطأ” — لقد كان قراراً معمارياً مبرراً في وقت إنشاء React Native في 2015. التطور الأصلي لمنصتين بلغتين مختلفتين تطلب صيغة تبادل عالمية. JSON كصيغة تسلسل كانت متاحة على جميع المنصات وسمحت بتوحيد التفاعل. أصبحت المشكلة واضحة لاحقاً، عندما بدأ استخدام React Native لتطبيقات معقدة بآلاف استدعاءات الوحدات الأصلية في الثانية.
يحافظ React Native على التوافق العكسي: الوحدات الأصلية المكتوبة لـ Bridge تستمر في العمل في البنية الجديدة عبر طبقة توافق. ومع ذلك، للوحدات الجديدة يُوصى باستخدام JSI مباشرة عبر TurboModules. ترحيل الوحدات الحالية يتضمن استبدال بروتوكول التفاعل دون تغيير منطق الأعمال للوحدة نفسها.
JSI هي طبقة أساسية تُبنى عليها جميع مكونات بنية React Native الجديدة. بدون JSI، لن يكون Fabric (العارض الجديد) ولا TurboModules (الوحدات الأصلية المحسّنة) ممكنين. يوفر JSI طريقة موحدة لتفاعل JS مع C++ على جميع المستويات.
Fabric هو العارض الجديد لـ React Native الذي يستخدم JSI للوصول المتزامن إلى تمثيلات واجهة المستخدم C++. في البنية القديمة، كان العرض يمر عبر Bridge: ينشئ JS عناصر React، يسلسلها إلى JSON، يرسلها عبر Bridge، يقوم الجانب الأصلي بإلغاء التسلسل وإنشاء واجهة المستخدم. Fabric عبر JSI ينشئ كائنات C++ لشجرة الظل (Shadow Tree) مباشرة من JS، ويحسب التخطيط بشكل متزامن عبر Yoga ويمرر الإطارات الجاهزة إلى العارض الأصلي — دون تسلسل واحد.
TurboModules هي تطور الوحدات الأصلية لـ React Native. بدلاً من تسجيل وحدة في Bridge واستدعاء طرقها عبر JSON، يستخدم TurboModules JSI للتحميل البطيء والاستدعاء المباشر. عندما يصل كود JS إلى وحدة لأول مرة، ينشئ JSI كائناً مضيفاً — يقوم بتحميل الوحدة الأصلية ويعرض طرقها كدوال C++. التحميل البطيء يعني أن الوحدة لا تستهلك ذاكرة حتى أول وصول — وهذا مهم بشكل خاص للتطبيقات ذات العشرات من الوحدات الأصلية، العديد منها يُستخدم فقط في شاشات محددة.
للعمل مع JSI في البنية الجديدة، يُستخدم Codegen — أداة تولد أغلفة C++ من مواصفات JavaScript. يصف المطور واجهة الوحدة الأصلية في TypeScript أو Flow، ويولد Codegen كود C++ ينفذ كائناً مضيفاً متوافقاً مع JSI. هذا يؤتمتة العمل الروتيني ويضمن مزامنة الأنواع في جانبي JS و C++.
لننظر كيف يبدو العمل مع JSI عملياً. في هذا المثال، ننشئ فئة C++ بسيطة يتم تصديرها إلى JS عبر JSI، ونستدعي طريقتها من كود JavaScript في React Native.
// Calculator.h — رأس فئة C++ يمكن الوصول إليها من JS
class Calculator {
public:
double add(double a, double b) { return a + b; }
double multiply(double a, double b) { return a * b; }
};
تحتوي فئة Calculator على طريقتين حسابيتين. نحتاج إلى جعلها قابلة للوصول من JS. للقيام بذلك، يتم إنشاء كائن مضيف يغلف Calculator ويعرض طرقه عبر JSI.
// CalculatorHostObject.cpp — تنفيذ الغلاف JSI
class CalculatorHostObject : public jsi::HostObject {
private:
Calculator calc;
public:
jsi::Value get(jsi::Runtime& runtime,
const jsi::PropNameID& name) override {
auto propName = name.utf8(runtime);
if (propName == "add") {
return jsi::Function::createFromHostFunction(
runtime, name, 2,
[this](jsi::Runtime& runtime,
const jsi::Value& thisVal,
const jsi::Value* args,
size_t count) -> jsi::Value {
return jsi::Value(calc.add(
args[0].asNumber(),
args[1].asNumber()));
});
}
return jsi::Value::undefined();
}
};
في هذا الكود، تُستدعى طريقة get كلما وصل JS إلى خاصية الكائن. إذا كان اسم الخاصية “add”، تُعاد دالة C++ تأخذ وسيطين من JS وتستدعي calc.add(). تُعاد القيمة كـ jsi::Value — يحول JSI تلقائياً double إلى رقم JS.
بعد تسجيل الكائن المضيف في بيئة JS، يبدو الاستدعاء كدالة JS عادية. يتم التحقق من جميع الأنواع في مرحلة توليد الكود، مما يلغي أخطاء عدم تطابق الأنواع أثناء وقت التشغيل.
// JavaScript — استدعاء حاسبة C++ عبر JSI
import { Calculator } from 'react-native-calculator'
const result = Calculator.add(5, 3)
console.log(result) // 8 — متزامن، بدون تأخير
const product = Calculator.multiply(4, 2.5)
console.log(product) // 10 — نتيجة فورية
ملاحظة: النتيجة تعاد فوراً، بدون Promise، بدون await، بدون ردود اتصال. هذا استدعاء متزامن كان مستحيلاً في بنية Bridge. للعمليات الطويلة (قراءة ملف، طلب شبكة)، يجب استخدام أنماط غير متزامنة — JSI لا تلغي الحاجة إلى خيوط خلفية للمهام الثقيلة.
عملياً، معظم المطورين لا يكتبون كائنات مضيفة JSI يدوياً — هذا العمل يقوم به Codegen، الذي يولد أغلفة C++ بناءً على مواصفات TypeScript. ومع ذلك، فهم كيفية عمل JSI داخلياً ضروري لتصحيح الأداء الفعال وعند إنشاء وحدات أصلية معقدة تتطلب وصولاً مباشراً لمكتبات C++ (Skia، FFmpeg، OpenCV).
الأسئلة الشائعة
يعمل Bridge بشكل غير متزامن عبر تسلسل JSON وقائمة انتظار رسائل — كل استدعاء يستغرق 3–10 مللي ثانية لتحويل البيانات. يوفر JSI وصولاً متزامناً مباشراً لكائنات C++ دون تسلسل، مما يقلل زمن الانتظار إلى 0.1–0.5 مللي ثانية. JSI يدعم أيضاً تمرير مراجع للكائنات بدلاً من نسخها.
نعم، يوفر JSI API C++ موحدة منفذة لـ Hermes (افتراضي React Native)، JavaScriptCore (iOS) و V8 (Android). لا يحتاج المطورون لكتابة كود مختلف لمحركات مختلفة — Fabric و TurboModules يعملان بنفس الطريقة على جميع المحركات المدعومة.
نعم، يوفر React Native طبقة توافق عكسي. الوحدات الأصلية المكتوبة لـ Bridge تستمر في العمل في البنية الجديدة. ومع ذلك، يُوصى بترحيلها إلى TurboModules للحصول على فوائد JSI — التحميل البطيء والاستدعاءات المتزامنة.
للتطوير اليومي — لا. مواصفات TypeScript للوحدات الأصلية تُترجم إلى أغلفة C++ تلقائياً عبر Codegen. معرفة C++ مطلوبة فقط عند إنشاء مكتبات C++ مخصصة أو عند تصحيح أداء JSI على مستوى وقت التشغيل.
يحل JSI ثلاث مشكلات رئيسية لـ Bridge: زمن الانتظار العالي بسبب تسلسل JSON، عدم وجود استدعاءات متزامنة، واستحالة تمرير كائنات معقدة بالمرجع. يتيح JSI أيضاً دمج مكتبات C++ مباشرة، دون وساطة Java أو Objective-C.
الخلاصة
سنقوم بتطوير تطبيق جوال جاهز
تقدم IT Sectr تطبيقات iOS وAndroid للشركات الناشئة والشركات منذ عام 2017. سوف نقدم لك النصح ونقترح أفضل حل.