使用 JSI 编写 Turbo 原生模块
为 Turbo 原生模块定义 TypeScript 规范文件,使用代码生成的接口在 Kotlin 和 Swift 中实现它,并通过 JSI 同步调用该模块。
使用 JSI 编写 Turbo 原生模块 是 CoddyKit 上的免费 React Native Academy 课时。 这是第 3 节课,共 4 节。 你可以在下方免费阅读本课时的完整内容 — 然后在浏览器中使用内置代码编辑器和全天候 AI 导师进行实践。 这是 React Native Academy 学习路径的一部分,你的进度在网页和 CoddyKit 应用中同步。 React Native Academy 课程共包含 4 节课。
本课时的部分内容尚未翻译,以英文显示。
The Problem with the Legacy Bridge
The legacy React Native bridge serializes every message between JavaScript and native into JSON, then deserializes it on the other side. This round-trip adds latency for every native call and blocks threads while serialization happens. For frequent calls like scroll events or animations, this overhead causes frame drops. JSI (JavaScript Interface) was created to eliminate this bottleneck.
What Is JSI
JSI (JavaScript Interface) is a lightweight C++ layer that lets JavaScript hold direct references to C++ host objects. Instead of sending a JSON-serialized message across the bridge, a JS call invokes a C++ function pointer directly and synchronously. This means native calls can be synchronous, zero-copy, and orders of magnitude faster than bridge-based calls.
Turbo Modules Architecture Overview
Turbo Native Modules are the new module system built on JSI. Key differences from legacy modules include: lazy loading (modules initialize only when first accessed instead of all at startup), typed interfaces (generated from a TypeScript spec so types are guaranteed on both sides), and synchronous calls possible without blocking the JS thread via JSI. The codegen tool generates the boilerplate C++ and native glue code automatically.
Writing the TypeScript Spec File
A Turbo Module starts with a TypeScript spec file that declares the module's interface. This file lives in your project and is processed by react-native-codegen to generate native interface files. The spec must import TurboModuleRegistry and define a type that extends TurboModule.
// NativeDeviceInfo.ts (spec file)
import type { TurboModule } from 'react-native';
import { TurboModuleRegistry } from 'react-native';
export interface Spec extends TurboModule {
getDeviceModel(): string;
getBatteryLevel(): Promise<number>;
addListener(eventType: string): void;
removeListeners(count: number): void;
}
export default TurboModuleRegistry.getEnforcing<Spec>('DeviceInfo');Running Codegen to Generate Native Code
After writing the TypeScript spec, you run React Native Codegen to generate the native interface files. For managed Expo projects, this happens automatically during expo prebuild. For bare React Native projects, run yarn react-native codegen. Codegen outputs Swift/Kotlin interfaces and C++ glue code in the build/generated folder that your native implementation must conform to.
# Bare React Native — run codegen manually
cd android && ./gradlew generateCodegenArtifactsFromSchema
# Then check generated files
ls android/app/build/generated/source/codegen/jni/
# NativeDeviceInfoJSI-generated.cpp
# NativeDeviceInfoJSI.hImplementing the Module in Kotlin
The Kotlin implementation must extend the codegen-generated abstract class (e.g., NativeDeviceInfoSpec) and override each method declared in the spec. Because the signatures are generated from TypeScript, there is no type mismatch between JS and native — the codegen enforces them at build time rather than at runtime.
// DeviceInfoModule.kt
package com.yourapp
import com.facebook.react.bridge.ReactApplicationContext
import com.yourapp.NativeDeviceInfoSpec
class DeviceInfoModule(context: ReactApplicationContext) :
NativeDeviceInfoSpec(context) {
companion object {
const val NAME = 'DeviceInfo'
}
override fun getName(): String = NAME
override fun getDeviceModel(): String {
return android.os.Build.MODEL
}
}Implementing the Module in Swift
The Swift implementation conforms to the codegen-generated protocol (e.g., NativeDeviceInfoSpec). Unlike legacy modules, there is no separate bridge .m file needed for Turbo Modules — the JSI layer handles the binding automatically once the module is registered. The protocol guarantees that the Swift method signatures match the TypeScript spec exactly.
// DeviceInfoModule.swift
import Foundation
@objc(DeviceInfoModule)
class DeviceInfoModule: NSObject, NativeDeviceInfoSpec {
func getDeviceModel() -> String {
return UIDevice.current.model
}
func getBatteryLevel(
_ resolve: @escaping RCTPromiseResolveBlock,
reject: @escaping RCTPromiseRejectBlock
) {
UIDevice.current.isBatteryMonitoringEnabled = true
resolve(UIDevice.current.batteryLevel * 100)
}
}Synchronous Calls via JSI
One of the biggest JSI benefits is the ability to call native synchronously from JS. When a Turbo Module method returns a non-Promise type (like string or number in the spec), JSI can return the value immediately without scheduling an async callback. This unlocks patterns like reading cached values synchronously that were impossible with the legacy bridge.
// TypeScript spec — synchronous return type
export interface Spec extends TurboModule {
getDeviceModel(): string; // synchronous
getBatteryLevel(): Promise<number>; // still async
}
// Usage in JS — getDeviceModel() is synchronous
import NativeDeviceInfo from './NativeDeviceInfo';
const model = NativeDeviceInfo.getDeviceModel(); // no await needed
console.log('Model:', model);Enabling the New Architecture
Turbo Modules require the New Architecture to be enabled in your project. For Android, set newArchEnabled=true in android/gradle.properties. For iOS, set RCT_NEW_ARCH_ENABLED=1 in the Podfile and run pod install. Expo SDK 50+ enables the new architecture automatically for managed workflow projects using the latest config.
# android/gradle.properties
newArchEnabled=true
# ios/Podfile — add before use_react_native!
ENV['RCT_NEW_ARCH_ENABLED'] = '1'
# Then reinstall pods
cd ios && pod installRegistering a Turbo Module
Turbo Modules are registered via a TurboModuleManagerDelegate instead of a ReactPackage. On Android you modify MainApplicationTurboModuleManagerDelegate.kt; on iOS you modify RCTAppDelegate.mm. The codegen also generates a ModuleProvider that wires up the module automatically when you follow the standard naming conventions.
// Android: MainApplicationTurboModuleManagerDelegate.kt
override fun getModule(
reactApplicationContext: ReactApplicationContext,
name: String
): NativeModule? {
return when (name) {
DeviceInfoModule.NAME -> DeviceInfoModule(reactApplicationContext)
else -> null
}
}Comparing Legacy vs Turbo Module Performance
Benchmarks show Turbo Modules can be 2–3x faster for high-frequency calls compared to legacy bridge modules. The improvement is most noticeable for calls made in rapid succession — such as gesture handlers or animation drivers. For infrequent calls like camera setup, the difference is negligible. Migrate to Turbo Modules when performance-critical native code needs to keep up with 60fps interactions.
// Performance benchmark pattern
const ITERATIONS = 10000;
const start = Date.now();
for (let i = 0; i < ITERATIONS; i++) {
// Synchronous JSI call
const model = NativeDeviceInfo.getDeviceModel();
}
const elapsed = Date.now() - start;
console.log('10k JSI calls in', elapsed, 'ms');
// Turbo: ~8ms vs Legacy bridge: ~80msQuick Check
Test your understanding of React Native Mobile Development concepts from this lesson.
Lesson Recap
In this lesson you learned: how JSI eliminates JSON serialization overhead from the legacy bridge, how to write a TypeScript spec file that codegen uses to generate native interfaces, and how to implement the generated spec in both Kotlin and Swift. You also saw how to enable the New Architecture and the performance benefits of synchronous JSI calls. Next up we explore async callbacks, promises, and native events.
常见问题解答
「使用 JSI 编写 Turbo 原生模块」课时是免费的吗?
是的 — 「使用 JSI 编写 Turbo 原生模块」的完整文本可在网页上免费阅读。要进行交互式练习(内置代码编辑器和全天候 AI 导师)并解锁 React Native Academy 课程的其余内容,请升级到 CoddyKit PRO。 React Native Academy 课程共包含 4 节课。
「使用 JSI 编写 Turbo 原生模块」这节课中我会学到什么?
为 Turbo 原生模块定义 TypeScript 规范文件,使用代码生成的接口在 Kotlin 和 Swift 中实现它,并通过 JSI 同步调用该模块。 你通过在浏览器中直接运行的动手代码来练习 React Native Academy,全天候 AI 导师会在你学习这节课的过程中回答你的问题。
学习 React Native Academy 需要有经验吗?
无需任何先前经验。CoddyKit 上的 React Native Academy 课程适合初学者到高级学习者,你可以从这里开始或从头开始,按照自己的节奏学习。 这是第 3 节课,共 4 节。
「使用 JSI 编写 Turbo 原生模块」课时需要多长时间?
大多数 CoddyKit 课程大约需要 5–10 分钟。每节课都很精短且互动,所以你能稳步进步,并在网页和应用中从离开的地方继续。
我能在这节 React Native Academy 课中编写并运行代码吗?
能。每节 React Native Academy 课都包含内置代码编辑器,你可以在浏览器中直接编写并运行真实代码,并获得即时 AI 反馈 — 无需本地设置。
此课程中的所有课时
- 使用 Kotlin 编写旧版原生模块
- 使用 Swift 编写旧版原生模块
- 使用 JSI 编写 Turbo 原生模块
- 来自原生代码的异步回调、Promise 与事件