Turbo Moduleは、JavaScript Interface(JSI)上に構築されたReact Nativeネイティブモジュールの進化形です。Bridgeとは異なり、Turbo Moduleは同期的に動作し、JSONシリアライゼーションが不要なため、大幅なパフォーマンス向上を実現します。Meta Engineering Blog、2024によると、Turbo Moduleは新しいアーキテクチャの主要コンポーネントであり、React Native 0.76+で利用可能です。
重要ポイント
Turbo Moduleは、JavaScriptとネイティブコード間の通信において従来のBridgeを置き換える、新しいReact Nativeアーキテクチャのコンポーネントです。すべてのメッセージがJSONにシリアライズされて非同期で送信されるBridgeとは異なり、Turbo ModuleはJSI(JavaScriptがC++で書かれた関数を直接呼び出せるようにするレイヤー)を使用します。
Turbo Moduleアーキテクチャは、MetaチームによってReact Conf 2021で、「The New Architecture」というコードネームの下でのより大規模なReact Native再構築の一部として発表されました。 Turbo Moduleに加えて、新しいアーキテクチャにはFabric(新しいレンダラー)、Codegen(コードジェネレーター)、JSI(インタラクションインターフェース)が含まれます。これらのコンポーネントは連携して、2015年からReact Nativeに蓄積されていたパフォーマンス問題を解決します。
Turbo Moduleは、従来のネイティブモジュールの3つの主要な問題を解決します:非同期性(すべての呼び出しがキューを通過する)、シリアライゼーション(各呼び出しにJSON変換が必要)、およびローディング(すべてのモジュールがアプリ起動時に初期化される)。Turbo Moduleを使用すると、ネイティブメソッドは同期的に呼び出され、データはコピーなしで転送され、モジュールはオンデマンドでロードされます。
JSI(JavaScript Interface)は、JavaScriptエンジン(HermesまたはJSC)とネイティブコードの間にレイヤーを作成するC++ APIです。JSIはホストオブジェクトを提供します。これは通常のJavaScriptオブジェクトやメソッドのように見えるC++オブジェクトです。JavaScriptがそのようなオブジェクトのメソッドを呼び出すと、JSIはシリアライゼーションやスレッド切り替えなしで、対応するC++コードを直接実行します。
JSIとBridgeの主な違いは、データコピーがないことです。Bridgeでは、各値がJSON文字列にシリアライズされ、キューを通過してデシリアライズされます。JSIはメモリ内のデータへのポインタを渡すため、パフォーマンスを損なうことなく大規模なデータを扱うことができます。Metaのテストによると、JSI呼び出しはBridge経由の同等の呼び出しよりも5〜10倍高速に実行されます。
JSIはHostObjectインターフェースを定義します。これはReact NativeがJS環境に通常のオブジェクトとして登録するC++クラスです。JSコードがプロパティにアクセスしたり、HostObjectのメソッドを呼び出したりすると、JSIが呼び出しをインターセプトしてC++コードを実行します。これにより、Bridgeのキューを待たずに同期的に操作を実行できます。
// JSI HostObjectの例 — 同期ネイティブ呼び出し
class ImageCompressorHostObject : public jsi::HostObject {
jsi::Value get(
jsi::Runtime& rt,
const jsi::PropNameID& propName
) override {
auto name = propName.utf8(rt);
if (name == "compressImage") {
return jsi::Function::createFromHostFunction(
rt,
jsi::PropNameID::forUtf8(rt, name),
2,
[](jsi::Runtime& rt,
const jsi::Value& thisValue,
const jsi::Value* args,
size_t count) -> jsi::Value {
return jsi::Value(rt, compressNative(
args[0].asString(rt).utf8(rt),
args[1].asNumber()
));
}
);
}
return jsi::Value::undefined();
}
};
上記のコードは、JSI HostObjectがJavaScriptからのcompressImage呼び出しをどのように処理するかを示しています。この関数は2つの引数(パス文字列と品質数値)を受け取り、ネイティブ関数compressNativeを呼び出して結果を返します。すべて同期的で、BridgeもJSONも不要です。これが従来のアーキテクチャに対するJSIの主な利点です。
JSIはReact Nativeメインランタイムの一部であり、特定のJavaScriptエンジンに依存しません。HermesとJavaScriptCore(JSC)の両方で動作し、ネイティブ環境との相互作用のための統一インターフェースを提供します。
Turbo ModuleとBridgeの比較は、React Nativeアーキテクチャの進化を示しています。Bridgeは2015年のReact Nativeの迅速な立ち上げのために設計されましたが、アプリケーションの複雑さが増すにつれて、その制限が重大になりました。Turbo Moduleはこれらの問題をアーキテクチャレベルで解決します。
| 特性 | Bridge | Turbo Module |
|---|---|---|
| 呼び出し速度 | 5~15msのオーバーヘッド | 0.1~0.5msのオーバーヘッド |
| 同期性 | 非同期のみ | 同期および非同期 |
| シリアライゼーション | 呼び出しごとにJSON | コピー不要のJSIオブジェクト |
| 型安全性 | なし | Codegen + TypeScript |
| 初期化 | 起動時に全モジュール | レイジー、オンデマンド |
実際には、ネイティブメソッドを頻繁に呼び出す場合(メディア処理やGPUを使用する場合など)に差が最も顕著です。まれな呼び出し(セッションごとに1回)の場合、パフォーマンスの差はごくわずかです。Turbo Moduleはまた、大きなバイナリデータ(画像、動画、バイナリプロトコル)の処理を簡素化します。Bridgeではファイルシステムを使った回避策が必要でした。
Turbo Moduleの作成は、TypeScriptで仕様を定義することから始まります。Codegenはこの仕様に基づいて、C++インターフェースとObjective-C/Javaスタブを自動生成します。これにより開発アプローチが完全に変わります。開発者はAPIを一度記述するだけで、Codegenが残りすべてを作成します。
// ImageCompressor.ts — Turbo Module仕様
import type { TurboModule } from 'react-native';
import type { Double } from 'react-native/Libraries/Types/CodegenTypes';
export interface Spec extends TurboModule {
compressImage(
imagePath: string,
quality: Double
): Promise<string>;
}
CodegenはTypeScriptの仕様を分析し、C++のHostObject、Objective-Cのプロトコル、Javaのインターフェースを生成します。開発者はネイティブロジックを実装するだけです。このアプローチにより、JavaScript側とネイティブ側の型が常に一致することが保証され、手動マッピングのエラーが排除されます。
// iOS用TurboModule実装(生成されたスタブ)
@interface ImageCompressorModule ()
RCT_EXPORT_MODULE(ImageCompressor)
@end
@implementation ImageCompressorModule
RCT_EXPORT_METHOD(compressImage:(NSString *)imagePath
quality:(NSNumber *)quality
resolver:(RCTPromiseResolveBlock)resolve
rejecter:(RCTPromiseRejectBlock)reject)
{
NSData *compressed = [ImageProcessor compressAtPath:imagePath
quality:quality.doubleValue];
resolve([NSString stringWithUTF8String:compressed.UTF8String]);
}
@end
重要な利点はレイジーローディングです。Turbo Moduleはアプリ起動時に初期化されるのではなく、JavaScriptから初めてアクセスされたときにのみ作成されます。これにより、すべてのネイティブモジュールが即座にロードされる従来のアプローチと比較して、アプリの起動時間が30〜50%短縮されます。
BridgeからTurbo Moduleへの移行は、アプリケーションを完全に書き換える必要はありません。React Native 0.73+は両方のアーキテクチャを並行してサポートしています。Bridgeモジュールは引き続き動作し、新しいモジュールはTurbo Moduleとして作成できます。これにより、モジュールごとに段階的に移行できます。
React Native 0.76+プロジェクトで新しいアーキテクチャを有効にするには、react-native.config.jsファイルでnewArchEnabledフラグをtrueに設定します。その後、既存のすべてのネイティブモジュールはBridge経由で引き続き動作し、新しいモジュールはTurbo Moduleとして作成できます。Codegenは両方のオプションを自動的に処理します。
// react-native.config.js — 新しいアーキテクチャを有効化
module.exports = {
project: {
ios: {},
android: {},
},
assets: [],
newArchEnabled: true,
};
JavaScriptから最も頻繁に呼び出されるモジュールから移行を開始することをお勧めします。それらが最大のパフォーマンス向上を得られます。まれにしか呼び出されないモジュール(セッションごとに1回)は、パフォーマンスを大幅に損なうことなくBridgeに残すことができます。
よくある質問
いいえ、Turbo Moduleは新しいアーキテクチャを有効にしたReact Native 0.73+が必要です。バージョン0.76以降、新しいアーキテクチャは安定し、本番プロジェクトに推奨されています。
はい、Turbo Moduleは両方のプラットフォームをサポートしています。JSIはクロスプラットフォームのC++レイヤーであり、CodegenはiOSとAndroid向けにそれぞれObjective-CとJavaのスタブを生成します。
いいえ、Turbo ModuleはJSIを介して任意のJSエンジンで動作します。ただし、HermesはJSIと新しいReact Nativeアーキテクチャで動作するように最適化されているため推奨されます。
Turbo Moduleのデバッグは、通常のネイティブコードと同様にXcodeまたはAndroid Studioを使用して行います。さらに、JSI呼び出しとモジュールのパフォーマンスを追跡するためにFlipperが利用可能です。
Turbo Moduleはアプリのサイズに実質的に影響しません。JSIのC++コードは既にReact Nativeの一部であり、生成されるコードは最小限で、ロジックの重複がないインターフェースのみです。
まとめ
ターンキー方式のモバイルアプリケーションを開発します
IT Sectrは2017年からスタートアップや企業向けにiOS・Androidアプリケーションを開発しています。私たちがご相談に乗り、最適なソリューションをご提案します。