JSI : ce que c'est, principe de fonctionnement et architecture

Auteur : IT Sectr Publié le : 2026-06-04 Temps de lecture : 10 min

JSI (JavaScript Interface) est une couche logicielle dans React Native qui fournit un accès synchrone direct depuis JavaScript aux objets et fonctions C++, remplaçant le pont JSON asynchrone Bridge. Contrairement à son prédécesseur, JSI permet d'appeler des méthodes natives sans sérialisation de messages et de passer des références à des objets C++ directement dans l'environnement JS. Selon React Native Team (2025), JSI offre une accélération jusqu'à 10 fois de l'interaction JS-code natif dans les scénarios à échange intensif de données.

Points clés

  • JSI — JavaScript Interface offrant un accès synchrone depuis JS au code natif C++
  • Accès direct élimine le besoin de sérialisation JSON et de file d'attente asynchrone
  • Performance de l'interaction JS-code natif augmente de 5 à 10 fois
  • Architecture JSI est le fondement de Fabric et TurboModules dans la nouvelle architecture React Native
  • Intégration C++ permet de connecter des bibliothèques C++ arbitraires sans wrappers natifs

Qu'est-ce que JSI ?

JSI (JavaScript Interface) est une couche C++ qui permet au moteur JavaScript (Hermes, JavaScriptCore, V8) d'accéder directement aux objets, fonctions et mémoire C++. Contrairement à Bridge, qui sérialisait les appels en JSON et les transmettait via une file d'attente asynchrone, JSI permet au code JS d'appeler des méthodes C++ de manière synchrone et d'obtenir le résultat immédiatement.

JSI a été introduit dans React Native 0.64 dans le cadre de la nouvelle architecture. L'objectif principal était d'éliminer le goulot d'étranglement que représentait Bridge : chaque interaction entre JS et le code natif consommait du temps en sérialisation, désérialisation et passage par la file d'attente des messages. JSI résout ce problème en donnant au moteur JS un accès direct aux objets C++ via des wrappers implémentant les interfaces jsi::Value, jsi::Object et jsi::Function.

JSI n'est pas un remplacement un pour un de Bridge — c'est une approche fondamentalement différente de l'intégration. Bridge fonctionnait comme une boîte aux lettres : JS envoyait un message, il passait par une file d'attente, le côté natif le traitait et envoyait une réponse. JSI fonctionne comme un pointeur : JS obtient une référence à un objet C++ et peut appeler ses méthodes de manière synchrone, comme des fonctions JS normales. C'est une différence fondamentale dans l'architecture d'interaction entre deux environnements.

Historique de création

Le besoin de JSI est né des limitations du Bridge original, établi dans React Native 2015. À mesure que le framework gagnait en popularité et que les applications devenaient plus complexes, le problème de performance est devenu évident : chaque appel à un module natif nécessitait au moins 3 à 5 ms pour la sérialisation. Pour des opérations simples comme lire la valeur d'un capteur ou obtenir la taille de l'écran, c'était acceptable, mais pour les animations, le travail graphique et le traitement de données en flux, c'était critique. L'équipe React Native a commencé à travailler sur la nouvelle architecture en 2019, et JSI en est devenu le fondement.

Support des moteurs JavaScript

JSI est conçu comme une couche d'abstraction au-dessus des moteurs JavaScript. Il fournit une API C++ unifiée qui est implémentée pour chaque moteur spécifique : Hermes, JavaScriptCore (iOS), V8 (Android). Cela signifie que les développeurs n'ont pas à se soucier des différences entre les moteurs — Fabric et TurboModules fonctionnent de la même manière quel que soit le moteur JS utilisé sous le capot.

Comment fonctionne JSI ?

Au cœur de JSI se trouve le concept d'objets hôtes (Host Objects) — des objets C++ exportés vers l'environnement JS en tant qu'objets JS natifs. Lorsque le code JS accède à une propriété ou méthode d'un tel objet, JSI intercepte l'appel et le délègue à la méthode C++ correspondante. Cela se produit de manière synchrone, dans le même fil, sans changement de contexte et sans allouer de mémoire pour une chaîne JSON.

Chaque objet hôte implémente l'interface jsi::HostObject avec les méthodes get, set et getPropertyNames. Le moteur JS appelle ces méthodes à chaque fois qu'une propriété de l'objet est accédée. Par exemple, lors de l'appel de NativeModule.someMethod() en JS, JSI convertit cet appel en un appel C++ vers la méthode correspondante de l'objet hôte. La valeur de retour est transmise à JS sous forme de jsi::Value — un type générique qui peut représenter un nombre, une chaîne, un booléen, un objet ou undefined.

Une caractéristique importante de JSI est l'absence de file d'attente de messages. Bridge utilisait une file d'attente asynchrone : JS envoyait une requête, passait à d'autres tâches, le côté natif traitait la requête, et le résultat était retourné via un callback. JSI fonctionne de manière synchrone : si JS appelle une méthode de module natif via JSI, l'exécution du code JS est suspendue jusqu'à la réception du résultat. Cela simplifie la logique (pas besoin d'attendre les callbacks) et élimine les conditions de concurrence, mais nécessite de la prudence — les appels synchrones longs bloquent le fil JS.

Cycle de vie des valeurs JSI

Les valeurs créées via JSI vivent dans l'environnement d'exécution du moteur JS et sont gérées par le ramasse-miettes. Lorsque le code C++ crée un jsi::String ou jsi::Object et le retourne à JS, l'environnement gère automatiquement la mémoire. Si le code C++ souhaite conserver une référence à une valeur JS entre les appels, on utilise jsi::Value::getWeak() ou un jsi::Object::setProperty global avec une référence stockée sur l'objet racine de l'environnement d'exécution. Cela empêche la suppression prématurée par le ramasse-miettes.

Sécurité des fils

JSI n'est pas thread-safe par défaut. Tous les appels de méthodes JSI doivent se produire depuis le fil où JS s'exécute (généralement le fil JS de React Native). Si un module natif lance un travail en arrière-plan sur un fil séparé, le résultat doit être renvoyé via le fil JS en utilisant runOnJS de TurboModules. Cette limitation est le prix de la synchronicité et de l'absence de sérialisation.

JSI vs Bridge : comparaison

La différence entre JSI et Bridge est fondamentale et affecte tous les aspects de l'interaction JS-code natif. Bridge était asynchrone, sérialisait les données en JSON et utilisait une file d'attente de messages ; JSI est synchrone, travaille avec des références natives et ne nécessite pas de sérialisation.

ParamètreBridgeJSI
Modèle d'appelFile d'attente asynchroneAppel direct synchrone
SérialisationJSON (sérialisation + désérialisation)Aucune (références directes aux objets C++)
Latence3 à 10 ms par appel0,1 à 0,5 ms par appel
TypageDynamique (via JSON)Statique (via Codegen)
Intégration C++Uniquement via les modules natifs (Java/ObjC)Directe, sans intermédiaires
FilFil natif séparéFil JS (synchrone)

Selon React Native Team, la migration de Bridge vers JSI dans l'application Facebook Marketplace a réduit le temps de démarrage de 35 % et diminué la consommation mémoire de 20 % en éliminant la duplication de données entre les côtés JS et natif.

Quand Bridge était nécessaire

Bridge n'était pas une « erreur » — c'était une décision architecturale justifiée au moment de la création de React Native en 2015. Le développement natif pour deux plateformes avec des langages différents nécessitait un format d'échange universel. JSON comme format de sérialisation était disponible sur toutes les plateformes et permettait d'unifier l'interaction. Le problème est devenu évident plus tard, lorsque React Native a commencé à être utilisé pour des applications complexes avec des milliers d'appels de modules natifs par seconde.

Rétrocompatibilité

React Native maintient la rétrocompatibilité : les modules natifs écrits pour Bridge continuent de fonctionner dans la nouvelle architecture via une couche de compatibilité. Cependant, pour les nouveaux modules, il est recommandé d'utiliser JSI directement via les TurboModules. La migration des modules existants implique de remplacer le protocole d'interaction sans modifier la logique métier du module lui-même.

JSI dans l'architecture React Native

JSI est une couche fondamentale sur laquelle sont construits tous les composants de la nouvelle architecture React Native. Sans JSI, ni Fabric (le nouveau rendu) ni TurboModules (modules natifs optimisés) ne seraient possibles. JSI fournit une manière unifiée pour JS d'interagir avec C++ à tous les niveaux.

Fabric et JSI

Fabric est le nouveau rendu React Native qui utilise JSI pour l'accès synchrone aux représentations C++ de l'interface utilisateur. Dans l'ancienne architecture, le rendu passait par Bridge : JS créait des éléments React, les sérialisait en JSON, les envoyait via Bridge, le côté natif désérialisait et créait l'interface utilisateur. Fabric via JSI crée des objets C++ Shadow Tree directement depuis JS, calcule de manière synchrone la mise en page via Yoga et transmet les trames prêtes au rendu natif — sans une seule sérialisation.

TurboModules et JSI

TurboModules est l'évolution des modules natifs React Native. Au lieu d'enregistrer un module dans Bridge et d'appeler ses méthodes via JSON, TurboModules utilise JSI pour le chargement différé et l'invocation directe. Lorsque le code JS accède à un module pour la première fois, JSI crée un objet hôte — il charge le module natif et expose ses méthodes comme des fonctions C++. Le chargement différé signifie que le module ne consomme pas de mémoire jusqu'au premier accès — ceci est particulièrement important pour les applications avec des dizaines de modules natifs, dont beaucoup ne sont utilisés que dans des écrans spécifiques.

Génération de code via JSI

Pour travailler avec JSI dans la nouvelle architecture, on utilise Codegen — un outil qui génère des liaisons C++ à partir de spécifications JavaScript. Le développeur décrit l'interface du module natif en TypeScript ou Flow, et Codegen génère du code C++ implémentant un objet hôte compatible JSI. Cela automatise le travail routinier et garantit que les types côté JS et C++ sont synchronisés.

Exemples de code avec JSI

Voyons à quoi ressemble le travail avec JSI en pratique. Dans cet exemple, nous créons une classe C++ simple qui est exportée vers JS via JSI, et nous appelons sa méthode depuis le code JavaScript dans React Native.

cpp
// Calculator.h — en-tête de classe C++ accessible depuis JS
class Calculator {
public:
    double add(double a, double b) { return a + b; }
    double multiply(double a, double b) { return a * b; }
};

La classe Calculator contient deux méthodes arithmétiques. Nous devons la rendre accessible depuis JS. Pour cela, un objet hôte est créé qui encapsule Calculator et expose ses méthodes via JSI.

cpp
// CalculatorHostObject.cpp — implémentation du wrapper 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();
    }
};

Dans ce code, la méthode get est appelée chaque fois que JS accède à une propriété de l'objet. Si le nom de la propriété est « add », une fonction C++ est retournée qui prend deux arguments de JS et appelle calc.add(). La valeur est retournée sous forme de jsi::Value — JSI convertit automatiquement le double en nombre JS.

Appel depuis JavaScript

Après avoir enregistré l'objet hôte dans l'environnement JS, l'appel ressemble à une fonction JS normale. Tous les types sont vérifiés à l'étape de génération de code, éliminant les erreurs d'incompatibilité de types lors de l'exécution.

js
// JavaScript — appel de la calculatrice C++ via JSI
import { Calculator } from 'react-native-calculator'

const result = Calculator.add(5, 3)
console.log(result) // 8 — synchrone, sans délai

const product = Calculator.multiply(4, 2.5)
console.log(product) // 10 — résultat immédiat

Remarque : le résultat est retourné immédiatement, sans Promise, sans await, sans callbacks. C'est un appel synchrone qui était impossible dans l'architecture Bridge. Pour les opérations longues (lecture de fichier, requête réseau), des motifs asynchrones doivent être utilisés — JSI n'élimine pas le besoin de fils d'arrière-plan pour les tâches lourdes.

En pratique, la plupart des développeurs n'écrivent pas manuellement les objets hôtes JSI — ce travail est effectué par Codegen, qui génère des wrappers C++ basés sur des spécifications TypeScript. Cependant, comprendre comment JSI fonctionne en interne est nécessaire pour un débogage efficace des performances et lors de la création de modules natifs complexes nécessitant un accès direct aux bibliothèques C++ (Skia, FFmpeg, OpenCV).

Questions fréquentes

En quoi JSI diffère-t-il de Bridge dans React Native ?

Bridge fonctionne de manière asynchrone via la sérialisation JSON et une file d'attente de messages — chaque appel prend 3 à 10 ms pour la conversion des données. JSI fournit un accès direct synchrone aux objets C++ sans sérialisation, réduisant la latence à 0,1 à 0,5 ms. JSI prend également en charge le passage de références d'objets plutôt que des copies.

JSI prend-il en charge tous les moteurs JavaScript ?

Oui, JSI fournit une API C++ unifiée implémentée pour Hermes (React Native par défaut), JavaScriptCore (iOS) et V8 (Android). Les développeurs n'ont pas besoin d'écrire du code différent pour différents moteurs — Fabric et TurboModules fonctionnent à l'identique sur tous les moteurs pris en charge.

Peut-on utiliser les anciens modules natifs avec JSI ?

Oui, React Native fournit une couche de rétrocompatibilité. Les modules natifs écrits pour Bridge continuent de fonctionner dans la nouvelle architecture. Cependant, il est recommandé de les migrer vers les TurboModules pour bénéficier des avantages de JSI — chargement différé et appels synchrones.

JSI nécessite-t-il des connaissances en C++ ?

Pour le développement quotidien — non. Les spécifications TypeScript des modules natifs sont compilées automatiquement en liaisons C++ via Codegen. Les connaissances en C++ ne sont nécessaires que lors de la création de bibliothèques C++ personnalisées ou lors du débogage des performances de JSI au niveau de l'environnement d'exécution.

Quels problèmes JSI résout-il ?

JSI résout trois problèmes clés de Bridge : la latence élevée due à la sérialisation JSON, l'absence d'appels synchrones et l'impossibilité de passer des objets complexes par référence. JSI permet également d'intégrer des bibliothèques C++ directement, sans intermédiaires Java ou Objective-C.

Résumé

  • JSI (JavaScript Interface) — technologie d'accès direct synchrone de JavaScript aux objets C++ dans React Native
  • Architecture JSI repose sur les objets hôtes — des objets C++ exportés vers JS en tant qu'objets JS natifs
  • Performance des appels via JSI est 10 à 50 fois supérieure à celle via Bridge, grâce à l'absence de sérialisation
  • Fabric et TurboModules — composants clés de la nouvelle architecture React Native, construits au-dessus de JSI
  • Synchronisme de JSI simplifie la logique du code mais nécessite de la prudence avec les opérations longues
  • Intégration C++ permet de connecter n'importe quelle bibliothèque native sans wrappers de plateforme
  • Utilisez JSI pour les modules natifs haute performance et migrez les existants depuis Bridge

Nous développerons une application mobile clé en main

IT Sectr crée des applications iOS et Android pour les startups et les entreprises depuis 2017. Nous vous conseillerons et vous proposerons la meilleure solution.

Discuter du projet

Lisez aussi