NativeModules cũ:
- Register tất cả module ngay lúc app start → tốn memory/startup time dù chưa dùng.
- Mọi method call async (Promise/callback) qua bridge.
- API định nghĩa qua RCTBridgeModule (iOS Obj-C) / ReactContextBaseJavaModule (Android) — không type-safe, JS phải nhớ chính xác signature.
- Hệ quả: app có 100 module, dù dùng 10, vẫn pay cost startup cho 100.
TurboModules:
- Lazy load: chỉ load module khi gọi đến lần đầu (require('NativeFoo').foo() → load native binding tại runtime).
- Sync call qua JSI: getNumber() return number ngay, không Promise. (Async vẫn được khi cần I/O.)
- Type-safe: Codegen sinh code C++/Java/Obj-C từ TypeScript spec → JS gọi sai signature → compile error.
- Backward compat: API JS có thể giữ giống module cũ — migrate dần.
Spec file (TypeScript):
// NativeCalculator.ts
import { TurboModule, TurboModuleRegistry } from 'react-native'
export interface Spec extends TurboModule {
add(a: number, b: number): number
fetchUser(id: string): Promise<{ name: string; age: number }>
}
export default TurboModuleRegistry.getEnforcing<Spec>('Calculator')Codegen đọc spec → tự sinh:
- iOS: RCTCalculatorSpec.h/.mm (Obj-C/C++ glue).
- Android: CalculatorSpec.java/.kt.
Native implement chỉ cần extends spec → mọi điểm khớp.
Performance: TurboModule call ~100× nhanh hơn legacy module call vì không serialize, không queue.
Old NativeModules:
- Every module registers at app start → cost startup time and memory even if unused.
- Every method call is async (Promise/callback) over the bridge.
- API defined via RCTBridgeModule (iOS Obj-C) / ReactContextBaseJavaModule (Android) — not type-safe, JS must remember the exact signature.
- Consequence: an app with 100 modules pays startup cost for 100 even if only 10 are used.
TurboModules:
- Lazy load: the module only loads on first call (require('NativeFoo').foo() → native binding loads at runtime).
- Sync calls via JSI: getNumber() returns immediately, no Promise. (Async still possible for I/O.)
- Type-safe: Codegen generates C++/Java/Obj-C from a TypeScript spec → wrong signature in JS becomes a compile error.
- Backward compatible: the JS API can stay the same as the legacy module — gradual migration.
Spec file (TypeScript):
// NativeCalculator.ts
import { TurboModule, TurboModuleRegistry } from 'react-native'
export interface Spec extends TurboModule {
add(a: number, b: number): number
fetchUser(id: string): Promise<{ name: string; age: number }>
}
export default TurboModuleRegistry.getEnforcing<Spec>('Calculator')Codegen reads the spec and emits:
- iOS: RCTCalculatorSpec.h/.mm (Obj-C/C++ glue).
- Android: CalculatorSpec.java/.kt.
The native implementation just extends the spec — everything stays in sync.
Performance: TurboModule calls are ~100x faster than legacy ones — no serialization, no queue.