1. 课程导入与工程全景

在 React Native 工程里,JavaScript 负责描述界面与业务逻辑,但真正贴近操作系统能力的事情——相机采集、蓝牙通信、文件加密、后台定位、原生支付 SDK——最终都要落到 Android 的 Java/Kotlin 或 iOS 的 Objective-C/Swift 代码上。原生模块(Native Module)就是连接这两侧的桥:它把原生平台的能力以方法调用的形式暴露给 JavaScript,让 JS 侧可以像调用普通异步函数一样使用平台能力。理解它的定位,是后续所有工程实践的前提。

需要先区分几个容易混淆的概念。JS 模块是纯 JavaScript 实现的模块,运行在 JS 引擎中,不接触原生代码,例如日期格式化工具、状态管理库。原生模块则包含原生实现,通过桥接机制被 JS 调用,典型形态是导出一个对象,对象上挂载若干方法,方法返回 Promise 或通过回调返回结果。TurboModule 是新一代原生模块规范,它用代码生成(Codegen)替代运行时反射,让 JS 与原生之间的调用具备静态类型约束和更低的调用开销,是当前推荐的模块形态。Fabric 则是新架构中的渲染器,负责 UI 组件(而非模块方法)的原生渲染,它与 TurboModule 同属新架构,但职责不同:Fabric 管“视图怎么画”,TurboModule 管“能力怎么调”。把这三者的边界理清,才不会在工程中把组件问题和模块问题混为一谈。

一句话定位:原生模块是 React Native 的“能力出口”,TurboModule 是它的现代规范,Fabric 是与之并列的“渲染入口”。

本课程的学习目标可以概括为四条主线。第一,能独立创建一个可被 JS 调用的原生模块,并完成 Android 与 iOS 双端注册。第二,能正确设计方法签名、参数类型与返回值形态,处理 Promise、回调、事件三种通信方式。第三,能理解并实践 TurboModule 的 Codegen 流程,写出带类型规范的模块。第四,能对模块进行调试、性能观察与常见故障排查,知道问题出在 JS 侧、桥接层还是原生侧。

整体知识框架可以按“从旧到新、从单端到双端、从功能到质量”来组织。基础层是传统 Native Module 的桥接机制与双端注册;进阶层是 TurboModule 规范、Codegen 与类型约束;并行层是 Fabric 与原生 UI 组件,用于理解模块与组件的分工;工程层则覆盖线程模型、事件发射、内存与生命周期、调试与性能。下面这张图给出本节最需要看清的关系全景:JS 侧、桥接层、原生侧三层的对象与数据流向,以及 TurboModule 与 Fabric 在新架构中的位置。

React Native 原生模块工程全景 JS 侧 · 桥接层 · 原生侧 三层对象与数据流向 JS 侧(JavaScript 线程) 业务 JS 模块 纯 JS,无原生依赖 NativeModules 调用 await Module.method(args) 事件监听 DeviceEventEmitter Promise / 回调 异步结果返回 桥接层(Bridge / JSI) 传统 Bridge JSON 序列化 · 异步批量 JSI(C++ 接口) 同步调用 · 共享内存 Codegen 生成接口 类型规范 → 原生桩代码 原生侧(Android / iOS) Android 原生模块 Kotlin / Java ReactContextBaseJavaModule @ReactMethod 标注 iOS 原生模块 Swift / Objective-C RCTBridgeModule 协议 RCT_EXPORT_METHOD 平台能力 / 第三方 SDK 相机 · 蓝牙 · 加密 · 支付 系统 API · 原生库 后台任务 · 传感器 调用请求 事件回传 Fabric 渲染器:负责 UI 组件的原生渲染,与 TurboModule 并列,共同构成新架构

从图中可以看到,一次典型的原生调用会经历“JS 发起 → 桥接层传递 → 原生执行 → 结果回传”的完整链路。传统 Bridge 使用 JSON 序列化并批量异步传递,适合大多数场景,但在高频调用或大数据量传输时会出现延迟与卡顿;JSI 则允许 JS 直接持有 C++ 对象引用,实现同步调用与更高效的数据共享,TurboModule 正是建立在 JSI 之上的模块规范。理解这条链路,才能在后续遇到“调用没反应”“返回慢”“事件丢失”等问题时,快速判断问题出在哪一层。

再看一个最小可感知的调用示例,帮助建立直觉。假设我们要暴露一个读取设备电量百分比的方法,JS 侧写法如下:

import { NativeModules } from 'react-native';

const { BatteryModule } = NativeModules;

async function showBattery() {
  try {
    const level = await BatteryModule.getBatteryLevel();
    console.log('当前电量:' + Math.round(level * 100) + '%');
  } catch (e) {
    console.warn('读取电量失败', e);
  }
}

这段代码里,BatteryModule 并不是 JS 实现的,而是原生侧注册后由桥接层注入到 NativeModules 上的对象。getBatteryLevel 返回 Promise,原生侧执行完成后把结果回传,JS 侧用 await 拿到 0 到 1 之间的浮点数。Android 侧对应的方法需要用 @ReactMethod 标注并返回 Promise,iOS 侧则通过 RCT_EXPORT_METHOD 导出并使用 resolve/reject 回调。这个例子虽小,却完整覆盖了“JS 调用、桥接传递、原生执行、异步返回”四个环节,后续章节会在此基础上逐步展开双端注册、参数类型、事件与 TurboModule 改造。

学习建议:每写一个原生模块,都先问自己三个问题——方法签名是否清晰?返回值用 Promise、回调还是事件?这个能力是否值得做成 TurboModule?把这三个问题想清楚,工程结构就不会乱。