JSI: ما هو، مبدأ العمل والبنية المعمارية

المؤلف: IT Sectr نُشر: 2026-06-04 وقت القراءة: 10 دق

JSI (JavaScript Interface) هي طبقة برمجية في React Native توفر وصولاً متزامناً مباشراً من JavaScript إلى كائنات ودوال C++، لتحل محل جسر JSON غير المتزامن Bridge. على عكس سابقه، يسمح JSI باستدعاء الطرق الأصلية دون تسلسل الرسائل وتمرير المراجع إلى كائنات C++ مباشرة إلى بيئة JS. وفقاً لـ فريق React Native (2025)، يوفر JSI تسارعاً يصل إلى 10 أضعاف في التفاعل بين JS والكود الأصلي في السيناريوهات كثيفة تبادل البيانات.

النقاط الرئيسية

  • JSI — JavaScript Interface يوفر وصولاً متزامناً من JS إلى كود C++ الأصلي
  • الوصول المباشر يلغي الحاجة إلى تسلسل JSON وقائمة انتظار الرسائل غير المتزامنة
  • الأداء للتفاعل بين JS والكود الأصلي يزيد بمقدار 5–10 أضعاف
  • البنية المعمارية JSI هو أساس Fabric و TurboModules في بنية React Native الجديدة
  • تكامل C++ يتيح ربط مكتبات C++ عشوائية بدون أغلفة أصلية

ما هو JSI؟

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 أساسها.

دعم محركات JavaScript

JSI مصممة كطبقة تجريد فوق محركات JavaScript. توفر API C++ موحدة يتم تنفيذها لكل محرك محدد: Hermes و JavaScriptCore (iOS) و V8 (Android). هذا يعني أن المطورين لا يحتاجون للقلق بشأن الاختلافات بين المحركات — Fabric و TurboModules يعملان بنفس الطريقة بغض النظر عن محرك JS المستخدم تحت الغطاء.

كيف يعمل JSI؟

في قلب 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

القيم التي تم إنشاؤها عبر 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: مقارنة

الفرق بين JSI و Bridge جوهري ويؤثر على جميع جوانب التفاعل بين JS والكود الأصلي. كان Bridge غير متزامن، يسلسل البيانات إلى JSON ويستخدم قائمة انتظار رسائل؛ JSI متزامن، يعمل مع المراجع الأصلية ولا يتطلب تسلسلاً.

المعلمةBridgeJSI
نموذج الاستدعاءقائمة انتظار غير متزامنةاستدعاء مباشر متزامن
التسلسلJSON (تسلسل + إلغاء تسلسل)لا يوجد (مراجع مباشرة لكائنات C++)
زمن الانتظار3–10 مللي ثانية لكل استدعاء0.1–0.5 مللي ثانية لكل استدعاء
الكتابةديناميكية (عبر JSON)ثابتة (عبر Codegen)
تكامل C++فقط عبر وحدات أصلية (Java/ObjC)مباشر، بدون وسطاء
الخيطخيط أصلي منفصلخيط JS (متزامن)

وفقاً لـ فريق React Native، أدى الانتقال من Bridge إلى JSI في تطبيق Facebook Marketplace إلى تقليل وقت بدء التشغيل بنسبة 35% وانخفاض استهلاك الذاكرة بنسبة 20% من خلال إلغاء ازدواجية البيانات بين جانبي JS والأصلي.

متى كان Bridge ضرورياً

لم يكن Bridge “خطأ” — لقد كان قراراً معمارياً مبرراً في وقت إنشاء React Native في 2015. التطور الأصلي لمنصتين بلغتين مختلفتين تطلب صيغة تبادل عالمية. JSON كصيغة تسلسل كانت متاحة على جميع المنصات وسمحت بتوحيد التفاعل. أصبحت المشكلة واضحة لاحقاً، عندما بدأ استخدام React Native لتطبيقات معقدة بآلاف استدعاءات الوحدات الأصلية في الثانية.

التوافق العكسي

يحافظ React Native على التوافق العكسي: الوحدات الأصلية المكتوبة لـ Bridge تستمر في العمل في البنية الجديدة عبر طبقة توافق. ومع ذلك، للوحدات الجديدة يُوصى باستخدام JSI مباشرة عبر TurboModules. ترحيل الوحدات الحالية يتضمن استبدال بروتوكول التفاعل دون تغيير منطق الأعمال للوحدة نفسها.

JSI في بنية React Native

JSI هي طبقة أساسية تُبنى عليها جميع مكونات بنية React Native الجديدة. بدون JSI، لن يكون Fabric (العارض الجديد) ولا TurboModules (الوحدات الأصلية المحسّنة) ممكنين. يوفر JSI طريقة موحدة لتفاعل JS مع C++ على جميع المستويات.

Fabric و JSI

Fabric هو العارض الجديد لـ React Native الذي يستخدم JSI للوصول المتزامن إلى تمثيلات واجهة المستخدم C++. في البنية القديمة، كان العرض يمر عبر Bridge: ينشئ JS عناصر React، يسلسلها إلى JSON، يرسلها عبر Bridge، يقوم الجانب الأصلي بإلغاء التسلسل وإنشاء واجهة المستخدم. Fabric عبر JSI ينشئ كائنات C++ لشجرة الظل (Shadow Tree) مباشرة من JS، ويحسب التخطيط بشكل متزامن عبر Yoga ويمرر الإطارات الجاهزة إلى العارض الأصلي — دون تسلسل واحد.

TurboModules و JSI

TurboModules هي تطور الوحدات الأصلية لـ React Native. بدلاً من تسجيل وحدة في Bridge واستدعاء طرقها عبر JSON، يستخدم TurboModules JSI للتحميل البطيء والاستدعاء المباشر. عندما يصل كود JS إلى وحدة لأول مرة، ينشئ JSI كائناً مضيفاً — يقوم بتحميل الوحدة الأصلية ويعرض طرقها كدوال C++. التحميل البطيء يعني أن الوحدة لا تستهلك ذاكرة حتى أول وصول — وهذا مهم بشكل خاص للتطبيقات ذات العشرات من الوحدات الأصلية، العديد منها يُستخدم فقط في شاشات محددة.

توليد الكود عبر JSI

للعمل مع JSI في البنية الجديدة، يُستخدم Codegen — أداة تولد أغلفة C++ من مواصفات JavaScript. يصف المطور واجهة الوحدة الأصلية في TypeScript أو Flow، ويولد Codegen كود C++ ينفذ كائناً مضيفاً متوافقاً مع JSI. هذا يؤتمتة العمل الروتيني ويضمن مزامنة الأنواع في جانبي JS و C++.

أمثلة كود مع JSI

لننظر كيف يبدو العمل مع JSI عملياً. في هذا المثال، ننشئ فئة C++ بسيطة يتم تصديرها إلى JS عبر JSI، ونستدعي طريقتها من كود JavaScript في React Native.

cpp
// 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.

cpp
// 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.

الاستدعاء من JavaScript

بعد تسجيل الكائن المضيف في بيئة 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).

الأسئلة الشائعة

كيف يختلف JSI عن Bridge في React Native؟

يعمل Bridge بشكل غير متزامن عبر تسلسل JSON وقائمة انتظار رسائل — كل استدعاء يستغرق 3–10 مللي ثانية لتحويل البيانات. يوفر JSI وصولاً متزامناً مباشراً لكائنات C++ دون تسلسل، مما يقلل زمن الانتظار إلى 0.1–0.5 مللي ثانية. JSI يدعم أيضاً تمرير مراجع للكائنات بدلاً من نسخها.

هل يدعم JSI جميع محركات JavaScript؟

نعم، يوفر JSI API C++ موحدة منفذة لـ Hermes (افتراضي React Native)، JavaScriptCore (iOS) و V8 (Android). لا يحتاج المطورون لكتابة كود مختلف لمحركات مختلفة — Fabric و TurboModules يعملان بنفس الطريقة على جميع المحركات المدعومة.

هل يمكن استخدام الوحدات الأصلية القديمة مع JSI؟

نعم، يوفر React Native طبقة توافق عكسي. الوحدات الأصلية المكتوبة لـ Bridge تستمر في العمل في البنية الجديدة. ومع ذلك، يُوصى بترحيلها إلى TurboModules للحصول على فوائد JSI — التحميل البطيء والاستدعاءات المتزامنة.

هل يتطلب JSI معرفة C++؟

للتطوير اليومي — لا. مواصفات TypeScript للوحدات الأصلية تُترجم إلى أغلفة C++ تلقائياً عبر Codegen. معرفة C++ مطلوبة فقط عند إنشاء مكتبات C++ مخصصة أو عند تصحيح أداء JSI على مستوى وقت التشغيل.

ما المشكلات التي يحلها JSI؟

يحل JSI ثلاث مشكلات رئيسية لـ Bridge: زمن الانتظار العالي بسبب تسلسل JSON، عدم وجود استدعاءات متزامنة، واستحالة تمرير كائنات معقدة بالمرجع. يتيح JSI أيضاً دمج مكتبات C++ مباشرة، دون وساطة Java أو Objective-C.

الخلاصة

  • JSI (JavaScript Interface) — تقنية وصول مباشر متزامن من JavaScript إلى كائنات C++ في React Native
  • البنية المعمارية JSI تعتمد على الكائنات المضيفة — كائنات C++ تُصدر إلى JS ككائنات JS أصلية
  • الأداء للاستدعاءات عبر JSI أعلى 10–50 مرة من Bridge، بفضل غياب التسلسل
  • Fabric و TurboModules — مكونات رئيسية لبنية React Native الجديدة، المبنية فوق JSI
  • التزامن في JSI يبسط منطق الكود لكنه يتطلب الحذر مع العمليات الطويلة
  • تكامل C++ يتيح ربط أي مكتبات أصلية بدون أغلفة منصة
  • استخدم JSI للوحدات الأصلية عالية الأداء ورحّل الموجودة من Bridge

سنقوم بتطوير تطبيق جوال جاهز

تقدم IT Sectr تطبيقات iOS وAndroid للشركات الناشئة والشركات منذ عام 2017. سوف نقدم لك النصح ونقترح أفضل حل.

مناقشة المشروع

اقرأ أيضًا