JSI (JavaScript Interface) — یک لایه نرمافزاری در React Native است که دسترسی مستقیم و همزمان از JavaScript به اشیاء و توابع C++ فراهم میکند و پل اسینکرون JSON یعنی Bridge را جایگزین میکند. به پیشینید خود میتواند روشهای ساختی را بدون سریالیسازی پیام فراخواند و ارجاعات به اشیاء C++ را مستقیماً به محیط JS منتقل کند. بطور دکل React Native Team (2025)، JSI تا 10 برابر شنابههای تعامل JS با کد ساختی را در سناریوهای تبادل شدید دادهها تضمین میکند.
نکات کلیدی
JSI (JavaScript Interface) — یک لایه C++ است که به موتور JavaScript (Hermes، JavaScriptCore، V8) امکان دسترسی مستقیم به اشیاء، توابع و حافظه C++ را میدهد. بر خلاف Bridge که فراخوانها را به JSON سریالی و آنها را از طریق یک صف اسینکرون انتقال میداد، JSI به کد JS اجازه میدهد روشهای C++ را به صورت همزمان فراخوانده و نتیجه را فوراً دریافت کند.
JSI در React Native 0.64 به عنوان بخشی از معماری جدید (New Architecture) معرفی شد. هدف اصلی برطرف کردن تنگناهی بود که 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 ms برای سریالیسازی نیاز داشت. برای عملیاتهای ساده مانند خواندن مقدار حسگر یا دریافت اندازه صفحه نمایش، این قابل قبول بود، اما برای انیمیشنها، کار با گرافیک و پردازش جریانی دادهها به صورت بحرانی. تیم 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 انجام میشود.
هر Host Object اینترفیس jsi::HostObject را با روشهای get، set و getPropertyNames پیاده میکند. موتور JS این روشها را در هر دسترسی به ویژگیهای شیء فراخوان میکند. به عنوان مثال، وقتی فراخوان NativeModule.someMethod() در JS انجام میشود، JSI آن را به فراخوان C++ روش مورد نظر Host Object تبدیل میکند. مقدار بازگشتی به JS به عنوان jsi::Value بازگردانده میشود — یک نوع عمومی که میتواند عدد، رشته، مقدار بولینی، شیء یا undefined را نمایش دهد.
ویژگی مهم JSI نبودن صف پیام است. Bridge از یک صف اسینکرون استفاده میکرد: JS درخواستی ارسال میکرد، به وظایف دیگر تغییر وضعیت میداد، طرف ساختی درخواست را پردازش و نتیجه از طریق فراخوان بازگشتی برمیگشت. JSI به صورت همزمان کار میکند: اگر JS روشی از یک ماژول ساختی را از طریق JSI فراخواند، اجرای کد JS تا دریافت نتیجه متوقف میشود. این منطق را ساده میکند (نیازی به انتظار کشیدن برای فراخوانهای بازگشتی نیست) و شرایط همزمانی را از بین میبرد، اما نیازمند احتیاط است — فراخوانهای همزمان طولانی تراز JS را مسدود میکنند.
مقادیری که از طریق JSI ایجاد شدهاند، در زمان اجرای موتور JS زندگی میکنند و توسط زباله گیر مدیریت میشوند. وقتی کد C++ jsi::String یا jsi::Object ایجاد و آن را به JS بازگرداند، محیط به صورت خودکار حافظه را مدیریت میکند. اگر کد C++ بخواهد ارجاعی به مقدار JS را بین فراخوانها حفظ کند، از jsi::Value::getWeak() یا جهانی jsi::Object::setProperty با حفظ ارجاع در شیء ریشه runtime استفاده میکند. این از حذف زودهنگام توسط زباله گیر جلوگیری میکند.
JSI به طور پیشفرض از نظر تراز ایمن نیست. همه فراخوانهای روشهای JSI باید از ترازی که JS در آن اجرا میشود (معمولاً تراز JS React Native) بیایند. اگر یک ماژول ساختی کار پسزمینه را در یک تراز جداگانه آغاز کند، نتیجه باید از طریق فراخوان runOnJS از TurboModules از طریق تراز JS بازگردانده شود. این محدودیت بهای همزمانی و عدم وجود سریالیسازی است.
تفاوت بین JSI و Bridge اساسی است و بر همه جوانب تعامل JS با کد ساختی تأثیر میگذارد. Bridge اسینکرون بود، دادهها را به JSON سریالی میکرد و از صف پیام استفاده میکرد؛ JSI همزمان است، با ارجاعات ساختی کار میکند و نیازی به سریالیسازی ندارد.
| پارامتر | Bridge | JSI |
|---|---|---|
| مدل فراخوان | صف اسینکرون | فراخوان مستقیم همزمان |
| سریالیسازی | JSON (سریالیسازی + درسریالیسازی) | ندارد (ارجاعات مستقیم به اشیاء C++) |
| تأخیر | 3–10 میلیثانیه بر فراخوان | 0.1–0.5 میلیثانیه بر فراخوان |
| تایپبندی | دینامیک (از طریق JSON) | استاتیک (از طریق Codegen) |
| اتصال C++ | فقط از طریق ماژولهای ساختی (Java/ObjC) | مستقیم، بدون واسطه |
| تراز | تراز ساختی جداگانه | تراز JS (همزمان) |
بطور دکل React Native Team، مهاجرت از 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 برای دسترسی همزمان به نمایشهای UI در C++ استفاده میکند. در معماری قدیمی، رندرینگ از طریق Bridge انجام میشد: JS عناصر React را ایجاد، آنها را به JSON سریالی، از طریق Bridge ارسال، طرف ساختی درسریالی و UI ایجاد میکرد. Fabric از طریق JSI اشیاء C++ Shadow Tree را مستقیماً از JS ایجاد، Layout را از طریق Yoga به صورت همزمان محاسبه و فریمهای آماده را به رندرکننده ساختی انتقال میدهد — بدون هیچ سریالیسازی.
TurboModules — تکامل ماژولهای ساختی React Native است. به جای ثبت ماژول در Bridge و فراخوان روشهای آن از طریق JSON، TurboModules از JSI برای بارگیری تنبل و فراخوان مستقیم استفاده میکند. وقتی کد JS برای اولین بار به ماژول دسترسی پیدا میکند، JSI یک Host Object ایجاد میکند — آن ماژول ساختی را بارگیری و روشهای آن را به عنوان توابع C++ ارائه میدهد. بارگیری تنبل به معنی است که ماژول تا اولین استفاده حافظه مصرف نمیکند — این به ویژه در برنامههایی با دهها ماژول ساختی که بسیاری از آنها فقط در صفحات مشخصی استفاده میشوند، مهم است.
برای کار با JSI در معماری جدید از Codegen استفاده میشود — ابزاری که پیچیدهسازیهای C++ را از مشخصات JavaScript تولید میکند. توسعهدهنده اینترفیس ماژول ساختی را در TypeScript یا Flow توصیف میکند، و Codegen کد C++ را که یک Host Object سازگار با JSI را پیاده میکند، تولید میکند. این کار روتین را خودکار کرده و تضمین میکند که انواع در طرف JS و C++ همگام شدهاند.
بیایید ببینیم کار با JSI در عمل چگونه است. در این مثال، یک کلاس C++ ساده ایجاد شده است که از طریق JSI به JS صادر شده و روش آن از کد 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 قابل دسترسی کنیم. برای این کار، یک Host Object ایجاد میکنیم که 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 تبدیل میکند.
پس از ثبت Host Object در محیط 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 Host Objects را دستی نمینویسند — این کار را 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 نوشته شدهاند، در معماری جدید به کار خود ادامه میدهند. برای دریافت مزایای JSI — بارگیری تنبل و فراخوانهای همزمان — توصیه میشود آنها را به TurboModules مهاجرت دهید.
برای توسعه روزمره — خیر. مشخصات TypeScript ماژولهای ساختی به طور خودکار توسط Codegen به پیچیدهسازیهای C++ کمپیل میشوند. دانش C++ فقط برای ایجاد کتاخانههای C++ خود یا اشکالزدایی عملکرد JSI در سطح runtime لازم است.
JSI سه مشکل کلیدی Bridge را حل میکند: تأخیر بالا به دلیل سریالیسازی JSON، نبود فراخوانهای همزمان و نتوانایی ارسال اشیاء پیچیده به صورت ارجاع. JSI همچنین امکان اتصال مستقیم کتاخانههای C++ را بدون لایه Java یا Objective-C فراهم میکند.
نتیجهگیری
ما یک اپلیکیشن موبایل به صورت کلید در دست توسعه خواهیم داد
IT Sectr از سال 2017 برنامههای iOS و Android را برای استارتاپها و کسبوکارها ایجاد میکند. ما به شما مشاوره میدهیم و بهترین راهحل را پیشنهاد خواهیم کرد.
همچنین بخوانید