Table of Contents
Introduction aux modules autochtones dans Réagir Native
Bien que le framework soit livré avec un riche ensemble de composants intégrés pour les éléments d'interface utilisateur communs et les API de périphériques de base, les applications de production nécessitent souvent des capacités au-delà de la fonction JavaScript par défaut de React Native. Les modules natifs servent de passerelle entre JavaScript et le code spécifique à la plate-forme écrit en Swift, Objective‐C, Kotlin ou Java. Ils débloquent l'accès aux capteurs matériels, aux services système, aux SDKs tiers et aux fonctionnalités sensibles aux performances qui seraient autrement difficiles ou impossibles à mettre en œuvre avec le JavaScript pur.
Dans ce guide, nous allons parcourir tout le cycle de vie d'un module natif – de la configuration de votre environnement et de l'écriture du code de plateforme, à l'enregistrement du module, à l'exposition des méthodes et à la consommation de JavaScript. Nous allons également couvrir les meilleures pratiques, les considérations de performance et des exemples réels tels que l'accès à la caméra, la gestion des tâches de fond et la communication entre les fils.
Comprendre les modules autochtones et le pont
À son cœur, l'architecture React Native ès s'appuie sur un pont qui sérialise et désérialise les messages entre le fil JavaScript et les fils natifs. Les modules natifs sont des objets qui vivent du côté natif et sont enregistrés avec le JavaScript runtime. Lorsque vous appelez une méthode sur un module natif de JS, le pont envoie un message au côté natif, exécute la méthode et renvoie le résultat (s'il y en a) à JS. Ce modèle vous permet de tirer parti des API système – par exemple, accéder au gyroscope de l'appareil, lire les balises NFC ou chiffrer les données à l'aide de la plate-forme enclave sécurisée – qui ne sont pas exposées par les bibliothèques standard React Native ès.
Les modules autochtones sont particulièrement utiles dans les scénarios suivants :
- Vous devez intégrer une bibliothèque native (p. ex. pour le traitement des paiements, l'apprentissage automatique ou la réalité augmentée) qui n'a pas d'emballage JavaScript.
- Vous avez besoin d'opérations à faible latence et à haute performance qui seraient ralenties par le fil JS (p. ex., traitement d'image, synthèse audio).
- Vous devez accéder aux fonctionnalités spécifiques à la plateforme qui ne font pas partie du cœur de React Native, comme le Prompt biométrique iOS HomeKit ou Android.
- Vous voulez partager le code entre une application React Native et une base de code native existante.
Avant de plonger dans la mise en œuvre, il est important de comprendre que les modules natifs ne sont pas une puce --ils augmentent la complexité, nécessitent des tests sur chaque plateforme, et peuvent introduire des préoccupations de sécurité de thread. Cependant, lorsqu'ils sont utilisés judicieusement, ils permettent le type de fonctionnalité avancée qui distingue votre application des concurrents multiplateformes plus simples.
Mettre en place votre environnement de développement
Pour créer des modules natifs, vous avez besoin de l'environnement de développement standard React Native pour Android et iOS. Cela inclut l'outillage Java/Kotlin pour Android (Android Studio, Gradle, Android SDK) et Xcode pour iOS avec CocoaPods (ou Swift Package Manager) pour la gestion de la dépendance. Assurez-vous d'avoir écrit React Native code avant de tenter des modules natifs – vous devriez être à l'aise avec les bases de la création d'un projet React Native et de comprendre la structure du fichier.
La plupart des développeurs commencent par un projet Natif React créé via . Dans le projet, les modules natifs sont généralement placés dans les répertoires et . Pour la modularité et la réutilisabilité, vous pouvez aussi envelopper votre module natif comme un paquet npm séparé – de nombreux modules communautaires suivent ce schéma.
Création d'un module natif : les bases
Chaque module natif se compose de deux parties : l'implémentation spécifique à la plateforme (un pour Android, un pour iOS) et le code de consommation JavaScript.
Android (Kotlin/Java)
Sur Android, vous créez une classe qui étend et implémente les méthodes que vous voulez exposer. Chaque méthode qui devrait être callable de JavaScript doit être annotée avec et avoir un type de retour (ou utiliser un callback/promise pour retourner des données).
package com.yourapp
import com.facebook.react.bridge.ReactApplicationContext
import com.facebook.react.bridge.ReactContextBaseJavaModule
import com.facebook.react.bridge.ReactMethod
import com.facebook.react.bridge.Promise
class MyNativeModule(reactContext: ReactApplicationContext) : ReactContextBaseJavaModule(reactContext) {
override fun getName(): String = "MyNativeModule"
@ReactMethod
fun doSomething(param: String, promise: Promise) {
try {
// Perform native work here
val result = "Hello from Kotlin! Received: $param"
promise.resolve(result)
} catch (e: Exception) {
promise.reject("ERROR", e.message)
}
}
}
Après avoir défini le module, vous devez l'enregistrer avec le système de paquets React Native. Créez une classe de paquets qui implémente :
package com.yourapp
import com.facebook.react.ReactPackage
import com.facebook.react.bridge.NativeModule
import com.facebook.react.bridge.ReactApplicationContext
import com.facebook.react.uimanager.ViewManager
class MyNativePackage : ReactPackage {
override fun createNativeModules(reactContext: ReactApplicationContext): List<NativeModule> {
return listOf(MyNativeModule(reactContext))
}
override fun createViewManagers(reactContext: ReactApplicationContext): List<ViewManager<*, *>> {
return emptyList()
}
}
Enfin, ajoutez le paquet à votre (ou ) à l'intérieur de la méthode :
override fun getPackages(): List<ReactPackage> {
val packages = PackageList(this).packages
packages.add(MyNativePackage())
return packages
}
iOS (Swift/Objectif‐C)
Sur iOS, les modules natifs sont généralement écrits en Objective‐C ou Swift. React Native s'attend à un fichier d'en-tête et une implémentation conformes au protocole . Pour Swift, vous devez créer un en-tête de transition. Voici un exemple d'objectif‐C :
// MyNativeModule.h
#import <React/RCTBridgeModule.h>
@interface MyNativeModule : NSObject <RCTBridgeModule>
@end
// MyNativeModule.m
#import "MyNativeModule.h"
#import <React/RCTLog.h>
@implementation MyNativeModule
RCT_EXPORT_MODULE();
RCT_EXPORT_METHOD(doSomething:(NSString *)param
resolver:(RCTPromiseResolveBlock)resolve
rejecter:(RCTPromiseRejectBlock)reject)
{
NSString *result = [NSString stringWithFormat:@"Hello from iOS! Received: %@", param];
resolve(result);
}
@end
Aucun enregistrement supplémentaire n'est nécessaire pour iOS – React Native découvre automatiquement des classes qui sont conformes à . Cependant, vous devez vous assurer que le fichier .m est compilé dans votre cible et que vous exécutez si vous ajoutez des dépendances natives.
Pour Swift, les étapes sont similaires mais nécessitent l'attribut et un en-tête de pontage. Une implémentation Swift typique ressemble à ceci:
import Foundation
@objc(MyNativeModule)
class MyNativeModule: NSObject {
@objc
func doSomething(_ param: String, resolver resolve: @escaping RCTPromiseResolveBlock, rejecter reject: @escaping RCTPromiseRejectBlock) {
let result = "Hello from Swift! Received: \(param)"
resolve(result)
}
}
N'oubliez pas d'importer dans votre en-tête de pont Objectif-C : .
Consommer des modules natifs à partir de JavaScript
Une fois votre module natif implémenté et enregistré, vous pouvez y accéder dans votre code JavaScript React Native via l'objet . Le nom que vous fournissez dans (Android) ou (iOS) devient la clé sous laquelle le module apparaît. Par exemple :
import { NativeModules } from 'react-native';
const { MyNativeModule } = NativeModules;
// Calling an exported method
MyNativeModule.doSomething('React Native')
.then(result => console.log(result))
.catch(error => console.error(error));
Vous pouvez également utiliser ou des gestionnaires d'événements si nécessaire. Notez que le nom de la méthode dans JavaScript doit correspondre exactement au nom de la méthode que vous avez exporté dans le code natif. Réagir Native les maps 1:1, ignorant le paramètre de nom de la méthode Android annotation , si vous le surchargez.
Pour les méthodes qui acceptent plusieurs paramètres, passez simplement comme arguments – le pont gérera la sérialisation. Les types de paramètres supportés comprennent , , , , (cartes) et les fonctions (callbacks). Cependant, en utilisant Promises via argument (Android) ou les blocs résolveur/réjecteur (iOS) est l'approche recommandée parce qu'il s'intègre proprement à la nature asynchrone de React Native.
Modèles de communication avancés
Bien que les appels de méthodes de base couvrent de nombreux cas d'utilisation, les fonctionnalités avancées nécessitent souvent des modèles de communication plus sophistiqués.
Révocations et promesses
Pour les opérations ponctuelles, les promesses sont les plus claires. Pour les flux de données continus (p. ex., lectures de capteurs), vous pouvez préférer les rappels. Cependant, les rappels sont moins courants chez les Réact Native modernes parce qu'ils peuvent conduire à l'enfer-callback. Un meilleur modèle pour les flux est d'émettre des événements du côté natif vers JavaScript via le (iOS) ou et (Android).
Émettre des événements de Native à JavaScript
Pour envoyer les données asynchronement du code natif à JS (par exemple, quand un capteur natif met à jour), vous pouvez utiliser l'émetteur d'événement singleton. Sur Android, obtenir une référence à et appeler :
reactContext.getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter::class.java)
.emit("onSensorUpdate", Arguments.createMap().apply {
putString("data", sensorValue)
})
Sur iOS, importez et remplacez les événements pris en charge :
@implementation MyNativeModule
RCT_EXPORT_MODULE();
- (NSArray<NSString *> *)supportedEvents {
return @[@"onSensorUpdate"];
}
// In your native method or delegate callback:
[self sendEventWithName:@"onSensorUpdate" body:@{@"data": sensorValue}];
@end
Dans JavaScript, inscrivez-vous à l'événement en utilisant :
import { NativeEventEmitter, NativeModules } from 'react-native';
const { MyNativeModule } = NativeModules;
const eventEmitter = new NativeEventEmitter(MyNativeModule);
eventEmitter.addListener('onSensorUpdate', (event) => {
console.log('Sensor data:', event.data);
});
N'oubliez pas de supprimer les auditeurs lorsque le composant se décolle pour éviter les fuites de mémoire.
Gestion des fils et performance
Par défaut, tous les appels de la méthode de module natif sont exécutés sur le pool de thread natif, séparés du thread d'interface utilisateur. Ceci est très bien pour la plupart des opérations, mais si vous devez mettre à jour l'interface utilisateur directement depuis votre module natif (par exemple, effectuer un calcul lourd et mettre à jour une vue), vous devez envoyer le travail au thread principal.
Pour les tâches de fond à long terme (comme la synchronisation de la base de données ou la numérisation Bluetooth), envisagez d'utiliser ou des callbacks pour éviter de bloquer le pont. Vous pouvez également créer des fils supplémentaires à l'intérieur de votre module natif, mais soyez prudent avec la sécurité du fil – l'état partagé doit être synchronisé.
Conseil de performance : minimiser la taille des données transmises par le pont. Les grands tableaux ou les blobs binaires (p. ex. images) doivent être traités avec des chemins de fichiers ou des références plutôt que sérialisés dans JSON.
Exemples réels mondiaux
Pour solidifier les concepts, nous allons examiner deux cas d'utilisation courante où les modules natifs sont indispensables.
Accès à la caméra
Un module natif peut envelopper la plate-forme de la caméra – par exemple Android API ou iOS. Votre module pourrait exposer des méthodes pour ouvrir l'aperçu de la caméra, capturer une photo et retourner le chemin d'image à JS. C'est exactement ce que font les bibliothèques populaires . En écrivant votre propre document, vous avez le contrôle total du traitement des images, des paramètres flash et de la mise au point manuelle.
Communication Bluetooth à faible énergie (BLE)
La connexion aux périphériques BLE (p. ex., moniteurs de fréquence cardiaque, balises) nécessite des API système natives. Un module natif peut rechercher des appareils, connecter, découvrir des services et lire/écrire des caractéristiques. Parce que les opérations BLE sont asynchrones et impliquent souvent des rappels, les événements qui émettent (comme ou ) sont le modèle naturel. Plusieurs modules BLE open-source existent, mais la construction de votre propre système vous permet d'exposer uniquement les fonctionnalités dont vous avez besoin et de vous donner le contrôle du threading.
Meilleures pratiques et pièges
- Ecrire des tests unitaires pour le code natif. Android Studio et Xcode fournissent des cadres de test (JUnit, XCTest). Testez les méthodes de votre module isolément avant de les intégrer à React Native.
- Les permissions de poignées gracieusement. De nombreux modules Natifs nécessitent des permissions d'exécution (caméra, emplacement, Bluetooth). Réagir Native=» fonctionne pour Android, mais pour iOS, vous pouvez avoir besoin de demander des permissions à l'intérieur de votre module natif ou utiliser une bibliothèque comme . Ne jamais supposer que les permissions sont accordées; gérer le rejet.
- Garder la lumière de la passerelle. Évitez de passer fréquemment de grands objets. Si vous devez diffuser des données haute fréquence (p. ex., capteur à 100Hz), envisager de mettre en lot des mises à jour ou de passer à un modèle où le code natif pré-traite les données et n'envoie que des résultats agrégés.
- Documentez attentivement votre API native module. Utilisez JSDoc ou un README pour décrire chaque méthode, ses paramètres, ses valeurs de retour et les différences de plate-forme.
- Utiliser seulement après l'initialisation de l'application. Parce que les modules natifs comptent sur le pont étant prêt, leur accès trop tôt (par exemple, dans un contexte global) peut entraîner . Toujours les accéder à l'intérieur d'un composant ou après qui attend le pont.
- L'écosystème React Native compte des milliers de paquets. Avant d'écrire votre propre module natif, recherchez ceux qui existent. Si vous trouvez un module qui correspond presque à vos besoins, envisagez de contribuer plutôt que de réinventer la roue.
- Consider TurboModules pour de nouveaux projets. À partir de React Native 0.68, la nouvelle architecture avec TurboModules offre de meilleures performances et un partage de mémoire JavaScript-native. Si vous démarrez une nouvelle application, évaluez en utilisant TurboModules (via la nouvelle architecture) au lieu de l'ancien pont.
Ressources extérieures
Pour plonger plus profondément dans le développement de modules natifs, consultez le Documentation native de réaction sur les modules natifs.Pour des conseils spécifiques à la plateforme, les guides de développeur Android et Apples documentation de développeur fournissent des références faisant autorité.
Conclusion
Les modules autochtones sont un outil puissant dans l'arsenal de développeurs autochtones de React, permettant l'accès à toute l'étendue des capacités de la plateforme. Bien qu'ils apportent une complexité supplémentaire – exigeant des connaissances de Java/Kotlin ou Swift/Objectif‐C, une gestion soigneuse des fils et des tests rigoureux – le gain est la capacité de fournir des fonctionnalités avancées qui seraient autrement impossibles.
Commencez petit : construisez un module simple de calcul qui multiplie deux nombres et retourne le résultat. Puis passez à des modules plus complexes comme un indicateur de niveau de batterie ou un filtre d'image personnalisé. Chaque module natif que vous créez approfondit votre compréhension de la plate-forme et vous fait un développeur mobile plus polyvalent.