JSI (JavaScript Interface) — egy szoftverréteg a React Native-ban, amely közvetlen szinkron hozzáférést biztosít JavaScript-ből C++ objektumokhoz és függvényekhez, felváltva az aszinkron JSON hidat, a Bridge-t. Elődjétől eltérően, a JSI lehetővé teszi natív metódusok meghívását üzenetek szerializálása nélkül, és C++ objektumokra mutató referenciák közvetlen átadását a JS környezetbe. A React Native Team (2025) adatai szerint a JSI akár 10-szeres gyorsulást is biztosít a JS és a natív kód közötti interakcióban intenzív adatcserével járó forgatókönyvekben.
Főbb pontok
JSI (JavaScript Interface) — egy C++ réteg, amely lehetővé teszi a JavaScript-motor (Hermes, JavaScriptCore, V8) számára, hogy közvetlenül hozzáférjen C++ objektumokhoz, függvényekhez és memóriához. A Bridge-től eltérően, amely a hívásokat JSON-ba szerializálta és egy aszinkron soron keresztül továbbította, a JSI lehetővé teszi a JS kódnak, hogy szinkron módon hívjon C++ metódusokat és azonnal megkapja az eredményt.
A JSI-t a React Native 0.64-ben vezették be az új architektúra (New Architecture) részeként. A fő cél a Bridge által okozott szűk keresztmetszet megszüntetése volt: a JS és a natív kód közötti minden interakció időt vett igénybe a szerializálásra, deszerializálásra és az üzenetsoron keresztüli továbbításra. JSI megoldja ezt a problémát azzal, hogy közvetlen hozzáférést biztosít a JS motornak a C++ objektumokhoz olyan burkolókon keresztül, amelyek megvalósítják a jsi::Value, jsi::Object és jsi::Function interfészt.
A JSI nem „egy-az-egyben” helyettesítője a Bridge-nek — ez egy alapvetően más megközelítés az integrációhoz. A Bridge úgy működött, mint egy postaláda: a JS elküldött egy üzenetet, az átment a soron, a natív oldal feldolgozta és választ küldött. A JSI úgy működik, mint egy mutató: a JS kap egy referenciát egy C++ objektumra, és szinkron módon hívhatja annak metódusait, mint a szokásos JS függvényeket. Ez az alapvető különbség a két környezet interakciójának architektúrájában.
A JSI iránti igény az eredeti Bridge korlátaiból fakadt, amelyet a 2015-ös React Native-ban hoztak létre. Ahogy a framework növekvő népszerűséggel és az alkalmazások egyre összetettebbé váltak, a teljesítményprobléma nyilvánvalóvá vált: minden natív modulhívásnak legalább 3–5 ms-ra volt szüksége a szerializáláshoz. Egyszerű műveletekhez, mint egy szenzor értékének olvasása vagy a képernyőméret lekérése, ez elfogadható volt, de animációkhoz, grafikai munkához és adatfolyam-feldolgozáshoz — kritikus. A React Native csapat 2019-ben kezdte meg az új architektúra munkálatait, és a JSI vált annak alapjává.
A JSI a JavaScript motorok feletti absztrakcióként lett tervezve. Egységes C++ API-t biztosít, amely minden konkrét motorhoz implementálva van: Hermes, JavaScriptCore (iOS), V8 (Android). Ez azt jelenti, hogy a fejlesztőnek nem kell aggódnia a motorok közötti különbségek miatt — a Fabric és a TurboModules ugyanúgy működik, függetlenül attól, hogy melyik JS motort használják.
A JSI alapjában a Host Objects koncepciója áll — C++ objektumok, amelyek natív JS objektumokként exportálódnak a JS környezetbe. Amikor a JS kód hozzáfér egy ilyen objektum tulajdonságához vagy metódusához, a JSI elfogja a hívást és továbbítja a megfelelő C++ metódushoz. Ez szinkron módon, ugyanabban a szálban történik, kontextusváltás és JSON karakterlánc memóriafoglalása nélkül.
Minden Host Object megvalósítja a jsi::HostObject interfészt a get, set és getPropertyNames metódusokkal. A JS motor ezeket a metódusokat hívja meg az objektum tulajdonságainak minden elérésekor. Például, amikor a NativeModule.someMethod() meghívásra kerül JS-ben, a JSI ezt a hívást a Host Object megfelelő metódusának C++ hívásává alakítja. A visszaadott érték jsi::Value-ként kerül vissza JS-be — egy általánosított típus, amely egy számot, karakterláncot, logikai értéket, objektumot vagy undefined-et képviselhet.
A JSI egyik fontos jellemzője az üzenetsor hiánya. A Bridge aszinkron sort használt: a JS elküldött egy kérést, átváltott más feladatokra, a natív oldal feldolgozta a kérést, és az eredmény egy callbacken keresztül érkezett vissza. A JSI szinkron módon működik: ha a JS egy natív modul metódusát hívja meg a JSI-n keresztül, a JS kód végrehajtása felfüggesztődik az eredmény megérkezéséig. Ez leegyszerűsíti a logikát (nem kell várni a callbackekre) és kiküszöböli a versenyfeltételeket, de óvatosságot igényel — a hosszú szinkron hívások blokkolják a JS szálat.
A JSI-n keresztül létrehozott értékek a JS motor futási környezetében élnek, és a szemétgyűjtő kezeli őket. Amikor a C++ kód létrehoz egy jsi::String vagy jsi::Object objektumot és visszaadja JS-be, a környezet automatikusan kezeli a memóriát. Ha a C++ kód meg akar tartani egy referenciát egy JS értékre a hívások között, akkor a jsi::Value::getWeak() vagy a globális jsi::Object::setProperty használja a referencia megtartásával a runtime gyökér objektumán. Ez megakadályozza a szemétgyűjtő általi idő előtti eltávolítást.
A JSI alapértelmezésben nem szálbiztos. Minden JSI metódushívásnak abból a szálból kell származnia, amelyben a JS fut (általában ez a React Native JS szála). Ha egy natív modul háttérmunkát indít egy külön szálban, az eredményt a JS szálon keresztül kell visszaküldeni a TurboModules runOnJS hívásával. Ez a korlátozás a szinkronitás és a szerializálás hiányának ára.
A JSI és a Bridge közötti különbség alapvető jellegű, és érinti a JS és a natív kód közötti interakció minden aspektusát. A Bridge aszinkron volt, szerializálta az adatokat JSON-ba és üzenetsort használt; a JSI szinkron, natív referenciákkal dolgozik és nem igényel szerializálást.
| Paraméter | Bridge | JSI |
|---|---|---|
| Hívási modell | Aszinkron sor | Szinkron közvetlen hívás |
| Szerializálás | JSON (szerializálás + deszerializálás) | Nincs (közvetlen referenciák C++ objektumokra) |
| Késleltetés | 3–10 ms hívásonként | 0.1–0.5 ms hívásonként |
| Típusosság | Dinamikus (JSON-en keresztül) | Statikus (Codegenen keresztül) |
| C++ integráció | Csak natív modulokon keresztül (Java/ObjC) | Közvetlen, közvetítők nélkül |
| Szál | Külön natív szál | JS szál (szinkron) |
A React Native Team adatai szerint a Bridge-ről JSI-re való migráció a Facebook Marketplace alkalmazásban 35%-kal csökkentette az indítási időt és 20%-kal csökkentette a memóriafogyasztást az adatok JS és a natív oldal közötti duplikálódásának megszüntetésével.
A Bridge nem „hiba” volt — egy építészeti döntés volt, amely a React Native 2015-ös létrehozásakor indokolt volt. A natív fejlesztés két különböző platformhoz, különböző nyelvekkel, univerzális csereformátumot igényelt. JSON szerializálási formátumként minden platformon elérhető volt, és lehetővé tette az interakció egységesítését. A probléma később jelentkezett, amikor a React Native-t összetett alkalmazásokhoz kezdték használni, másodpercenként ezreket meghaladó natív modulhívással.
A React Native megtartja a visszafelé kompatibilitást: a Bridge-hez írt natív modulok továbbra is működnek az új architektúrában egy kompatibilitási rétegen keresztül. Az új modulokhoz azonban ajánlott azonnal a JSI használata a TurboModules-on keresztül. A meglévő modulok migrációja az interakciós protokoll cseréjéből áll anélkül, hogy a modul üzleti logikáját megváltoztatnák.
A JSI az az alapvető réteg, amelyre a React Native új architektúrájának összes komponense épül. JSI nélkül sem a Fabric (új renderelő), sem a TurboModules (optimalizált natív modulok) nem működhetne. A JSI egységes módot biztosít a JS és a C++ közötti interakcióra minden szinten.
Fabric — az új React Native renderelő, amely a JSI-t használja a C++ UI megjelenítések szinkron eléréséhez. A régi architektúrában a renderelés a Bridge-en keresztül történt: a JS létrehozta a React elemeket, szerializálta őket JSON-ba, elküldte a Bridge-en keresztül, a natív oldal deszerializálta és létrehozta a UI-t. A Fabric a JSI-n keresztül közvetlenül JS-ből hozza létre a C++ Shadow Tree objektumokat, szinkron módon számolja ki a Layout-ot a Yoga segítségével, és továbbítja a kész kereteket a natív renderelőnek — egyetlen szerializálás nélkül.
TurboModules — a natív React Native modulok evolúciója. Ahelyett, hogy a modult a Bridge-ben regisztrálnák és a metódusait JSON-on keresztül hívnák, a TurboModules a JSI-t használja lusta betöltéshez és közvetlen híváshoz. Amikor a JS kód először hozzáfér egy modulhoz, a JSI létrehoz egy Host Objectet — ez betölti a natív modult és annak metódusait C++ függvényekként kínálja. Lusta betöltés azt jelenti, hogy a modul nem fogyaszt memóriát az első használatig — ez különösen fontos olyan alkalmazásokban, amelyek tucatnyi natív modult tartalmaznak, amelyek közül sokat csak bizonyos képernyőkön használnak.
A JSI-vel való munkához az új architektúrában a Codegen-t használják — egy eszközt, amely JavaScript specifikációkból generál C++ burkolókat. A fejlesztő leírja a natív modul interfészét TypeScript vagy Flow nyelven, és a Codegen C++ kódot generál, amely egy JSI-kompatibilis Host Objectet valósít meg. Ez automatizálja a rutinmunkát és garantálja, hogy a típusok a JS és a C++ oldalon szinkronizálva legyenek.
Nézzük meg, hogyan néz ki a JSI-vel való munka a gyakorlatban. Ebben a példában létrehozunk egy egyszerű C++ osztályt, amely JSI-n keresztül exportálódik JS-be, és annak metódusát a React Native JavaScript kódjából hívjuk meg.
// Calculator.h — C++ osztály fejléc, elérhető JS-ből
class Calculator {
public:
double add(double a, double b) { return a + b; }
double multiply(double a, double b) { return a * b; }
};
A Calculator osztály két aritmetikai metódust tartalmaz. El kell érnünk, hogy JS-ből is elérhető legyen. Ehhez létrehozunk egy Host Objectet, amely becsomagolja a Calculator-t és annak metódusait JSI-n keresztül kínálja.
// CalculatorHostObject.cpp — JSI burkoló implementáció
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();
}
};
Ebben a kódban a get metódust minden alkalommal meghívja a rendszer, amikor a JS hozzáfér az objektum tulajdonságához. Ha a tulajdonság neve „összeadás”, akkor egy C++ függvény kerül visszaadásra, amely két argumentumot fogad JS-ből és meghívja a calc.add()-ot. Az érték jsi::Value-ként kerül visszaadásra — a JSI automatikusan konvertálja a double-t JS számmá.
Miután a Host Object regisztrálásra került a JS környezetben, a hívás egy szokásos JS függvényhez hasonlít. Minden típus ellenőrzésre kerül a kódgenerálás fázisában, ami kiküszöböli a típuseltérési hibákat futási időben.
// JavaScript — C++ számológép hívása JSI-n keresztül
import { Calculator } from 'react-native-calculator'
const result = Calculator.add(5, 3)
console.log(result) // 8 — szinkron, késleltetés nélkül
const product = Calculator.multiply(4, 2.5)
console.log(product) // 10 — azonnali eredmény
Figyelje meg: az eredmény azonnal, Promise, await és callbackek nélkül érkezik. Ez egy szinkron hívás, amely lehetetlen volt a Bridge architektúrában. Hosszó időt igénylő műveletekhez (fájl olvasása, hálózati kérés) aszinkron mintákat kell használni — a JSI nem szünteti meg a háttérszálak szükségességét a nehéz feladatokhoz.
A gyakorlatban a legtöbb fejlesztő nem ír JSI Host Objecteket kézzel — ezt a munkát a Codegen végzi, amely TypeScript specifikációk alapján generál C++ burkolókat. Azonban annak megértése, hogy a JSI hogyan működik a motorház alatt, elengedhetetlen a hatékony teljesítmény-hibakereséshez és az olyan összetett natív modulok létrehozásához, amelyek közvetlen hozzáférést igényelnek C++ könyvtárakhoz (Skia, FFmpeg, OpenCV).
Gyakran Ismételt Kérdések
A Bridge aszinkron módon működik JSON szerializáláson és üzenetsoron keresztül — minden hívás 3–10 ms-ot igényel az adatkonverzióhoz. A JSI szinkron közvetlen hozzáférést biztosít a C++ objektumokhoz szerializálás nélkül, 0.1–0.5 ms-ra csökkentve a késleltetést. JSI támogatja az objektumok referenciáinak átadását is, nem a másolataikét.
Igen, a JSI egységes C++ API-t biztosít, amely a Hermes (alapértelmezett a React Native-ban), a JavaScriptCore (iOS) és a V8 (Android) számára van implementálva. A fejlesztőnek nem kell különböző kódot írnia különböző motorokhoz — a Fabric és a TurboModules ugyanúgy működik az összes támogatott motoron.
Igen, a React Native visszafelé kompatibilitási réteget biztosít. A Bridge-hez írt natív modulok továbbra is működnek az új architektúrában. Javasolt azonban ezek migrálása a TurboModules-ra a JSI előnyeinek — lusta betöltés és szinkron hívások — kihasználása érdekében.
A mindennapi fejlesztéshez — nem. A natív modulok TypeScript specifikációi automatikusan C++ burkolókká fordulnak a Codegen-en keresztül. C++ ismeretekre csak saját C++ könyvtárak létrehozásakor vagy a JSI teljesítményének futási idő szintű hibakeresésekor van szükség.
A JSI három fő problémát old meg a Bridge-ben: a JSON szerializálás miatti magas késleltetést, a szinkron hívások hiányát és az összetett objektumok referencia útján történő átadásának lehetetlenségét. A JSI lehetővé teszi továbbá a C++ könyvtárak közvetlen integrációját, Java vagy Objective-C réteg nélkül.
Összefoglalás
Kulcsrakész mobilalkalmazást fejlesztünk
Az IT Sectr 2017 óta készít iOS és Android alkalmazásokat induló vállalkozásoknak és vállalkozásoknak. Tanácsot adunk, és a legjobb megoldást javasoljuk.
Olvassa el is