刚开始使用 React Native 时,很容易产生"不知道为什么就跑起来了"的感觉。本文将从 Metro、JSI、Codegen、Expo 的内部运作机制,到 Expo SDK 无法覆盖的原生实现领域,进行一体化的深度解析。
1. React Native 的核心理念
React Native 的核心在于将用 JS 编写的 UI 逻辑转换为原生 UI 组件。就像 ReactDOM 将 <div> 转换为 DOM 元素一样,React Native 将 <View> 和 <Text> 映射到 iOS 的 UIView 和 Android 的 android.view.View。
也就是说,它生成的是真正的原生 UI,而不是在 WebView 中渲染 HTML。这是与 Cordova 或 Ionic 的根本区别。
2. 架构演进:从 Bridge 到 JSI
React Native 目前存在两种并行的架构。
flowchart LR
subgraph OLD["旧架构(Bridge)"]
direction TB
J1["JS 线程<br/>React 逻辑"] -->|"JSON 序列化"| BR["Bridge<br/>异步队列 ⚠"]
BR -->|"JSON 反序列化"| N1["原生线程<br/>UIKit / Android View"]
SH["Shadow 线程<br/>Yoga 布局"] --> N1
end
subgraph NEW["新架构(JSI)"]
direction TB
HM["Hermes 引擎<br/>字节码执行"] -->|"直接 C++ 引用"| JSI["JSI<br/>Host Objects"]
JSI --> FAB["Fabric<br/>同步渲染器"]
JSI --> TM["TurboModules<br/>懒加载初始化"]
FAB --> N2["原生层"]
TM --> N2
end旧架构(Bridge)的问题
旧版 React Native 的 JS 线程和原生线程通过 Bridge 这一异步的单点瓶颈进行通信。所有交互都需要序列化为 JSON 字符串、发送、再反序列化,这一往返过程导致动画、手势等高频操作容易出现性能问题。
新架构(JSI + Hermes)的革新
JSI(JavaScript Interface)是一个用 C++ 实现的轻量绑定层,使 JS 引擎能够直接同步引用原生对象。这带来了以下变革:
- Fabric — 可同步构建和更新 UI 树的新渲染器
- TurboModules — 延迟初始化,直到需要时才加载原生模块
- Hermes — Facebook 开发的引擎,将 JS 预编译为字节码,大幅缩短启动时间
3. Codegen:类型安全原生通信的核心
Codegen 是新架构中最容易被忽视的机制。JSI 和 TurboModules 让通信成为可能,而 Codegen 则在构建时保证通信正确无误。
flowchart TD
SPEC["JS Spec 文件<br/>用 TypeScript / Flow 定义类型"]
subgraph BUILD["构建时"]
CG["Codegen<br/>在 pod install / Gradle 时执行"]
end
subgraph OUT["生成的产物"]
CPP["C++ 抽象类<br/>TurboModule 基础接口"]
DESC["Component Descriptor<br/>用于 Fabric 原生组件"]
JSTYPE["JS 类型定义<br/>等同于 .d.ts"]
end
subgraph NATIVE["原生实现"]
IOS["iOS<br/>使用 Swift / Obj-C 实现"]
AND["Android<br/>使用 Kotlin / Java 实现"]
end
RUNTIME["通过 JSI<br/>类型安全、同步调用"]
SPEC --> CG
CG --> CPP & DESC & JSTYPE
CPP --> IOS & AND
DESC --> IOS & AND
IOS --> RUNTIME
AND --> RUNTIME在旧架构中,JS 与原生的接口几乎是"君子协定"。从 JS 调用 NativeModules.MyModule.doSomething(42),如果原生期望接收 String,直到运行时崩溃才会发现问题。
Codegen 在构建时解决了这个问题。开发者只需用 TypeScript 或 Flow 编写 Spec(规格说明),描述原生模块接收何种类型。在 pod install(iOS)或 Gradle 构建(Android)时,Codegen 自动生成 C++ 抽象类、iOS 和 Android 的原生模板代码以及 JS 侧的类型定义。
4. Metro Bundler 的处理流程
Metro 是 React Native 专用的 JavaScript 打包器。它与 webpack 承担相同的角色,但针对移动端开发进行了优化。
flowchart LR
SRC[".js / .ts / .tsx<br/>源文件"]
RES["解析<br/>import 路径解析"]
TRA["转换<br/>Babel ES 转换"]
SER["序列化<br/>生成 Bundle"]
DEV["发送到设备<br/>HTTP :8081"]
HMR["HMR<br/>仅重发变更模块"]
CACHE["文件系统缓存<br/>第二次起速度更快"]
SRC --> RES --> TRA --> SER --> DEV
TRA -.->|"检测文件变更"| HMR
HMR -.->|"保留状态实时反映"| DEV
TRA <-.->|"缓存转换结果"| CACHEMetro 的处理分为三个阶段:
- 解析 — 类似 Node.js 的模块解析,自动选择
foo.ios.ts/foo.android.ts等平台特定文件 - 转换 — Babel 转换 JSX、TypeScript 和现代 ES;结果缓存到文件系统,后续启动更快
- 序列化 — 生成最终 Bundle,通过 HTTP 服务器发送到设备
值得注意的是 HMR(热模块替换)。只更新发生变更的模块图谱,可以在保留 React 组件状态的同时立即反映代码变更。生产构建将 --minify 与 Hermes 字节码编译结合,同时减小文件大小和启动时间。
5. Expo 的层次结构与开发工具
flowchart TD
APP["你的应用<br/>App.tsx / screens / hooks"]
SDK["Expo SDK<br/>expo-camera / expo-location / expo-router …"]
EMC["expo-modules-core<br/>Swift / Kotlin DSL 类型安全描述"]
RN["React Native Core<br/>View / Text / Animated / StyleSheet"]
IOS["iOS<br/>UIKit / AVFoundation / CoreLocation"]
AND["Android<br/>View / Camera2 / LocationManager"]
GO["Expo Go<br/>扫描二维码即可启动"]
EASB["EAS Build<br/>在云端生成 .ipa / .apk"]
EASU["EAS Update<br/>通过 OTA 仅发布 JS Bundle"]
STORE["App Store / Google Play"]
APP --> SDK
EMC --> SDK
SDK --> RN
RN --> IOS & AND
GO -->|"开发时托管"| APP
EASB --> STORE
EASU -->|"无需 App Store 审核"| APPExpo SDK 将相机、位置、通知、文件系统等原生功能以版本化包的形式提供。无需自行编写原生代码,这是它最大的价值所在。
expo-modules-core 是一种较新的机制,使用 Swift 和 Kotlin DSL 可以声明式地编写原生模块,并与 Codegen 集成。比旧的 NativeModules 桥接方式更类型安全、代码量更少,Expo 的几乎所有第三方 SDK 都使用它构建。
EAS(Expo Application Services) 是 Build、Update、Submit、Workflows 等多种服务的总称。本文主要介绍 EAS Build 和 EAS Update。EAS Build 在云端构建原生二进制文件,无需 Mac 即可完成 iOS 构建。EAS Update 通过 OTA 发布 JS Bundle 和资产,可快速推送不涉及原生代码的修改。
6. Expo 无法覆盖的领域与工作流选择
Expo SDK 主要无法覆盖以下领域:
- 蓝牙 LE / NFC — 平台特定的外设通信
- 自定义支付/生物识别 SDK — 直接集成供应商
.framework/.aar - 实时音视频处理 — WebRTC 或自定义编解码器的底层操作
- 内部原生库 — 从现有应用中提取的 Swift / Kotlin 资产
- 自定义相机视图 — 使用 OpenGL / Metal 的自定义渲染
flowchart TD
START["需要什么原生功能?"]
START --> Q1{"Expo SDK / 公开库<br/>能否覆盖?"}
Q1 -->|"是"| MANAGED["Managed Workflow<br/>无需原生代码<br/>只需 npx expo start"]
Q1 -->|"否"| Q2{"只需配置变更?<br/>(Info.plist / AndroidManifest 追加等)"}
Q2 -->|"是"| PLUGIN["创建 Config Plugin<br/>追加到 app.json<br/>prebuild 自动应用"]
Q2 -->|"否"| Q3{"自定义原生代码的规模?"}
Q3 -->|"轻量<br/>(封装现有 SDK)"| BARE["Bare Workflow<br/>npx expo prebuild<br/>→ 直接管理 ios/ android/"]
Q3 -->|"大规模<br/>(自定义模块 / UI)"| MODULE{"构建什么?"}
MODULE -->|"逻辑类<br/>蓝牙 / 支付 / 加密"| TM["TurboModule<br/>+ Codegen"]
MODULE -->|"UI 组件类<br/>自定义相机视图等"| FAB["Fabric Component<br/>+ Codegen"]
BARE --> TM & FAB
PLUGIN --> BARE轻微的配置变更可以用 Config Plugin 处理。但如果需要编写代码,通过 npx expo prebuild 生成 ios/ 和 android/ 目录并迁移到 Bare Workflow,是现实的起点。
7. 整体架构:JSI + Codegen + 原生实现
flowchart TB
subgraph JS["JS 层(Hermes)"]
APP["应用代码 / React 组件"]
SPEC["JS Spec<br/>(TypeScript + TurboModuleRegistry)"]
end
subgraph CODEGEN["Codegen(构建时自动生成)"]
GEN_CPP["C++ 抽象类<br/>TurboModule / ComponentDescriptor"]
GEN_SHADOW["ShadowNode / Props 定义"]
end
subgraph JSI_LAYER["JSI 层(C++)"]
JSI["JSI Host Object"]
FABRIC["Fabric 渲染器"]
end
subgraph NATIVE_IOS["iOS 原生实现"]
SWIFT["Swift / Obj-C 实现类<br/>(继承 C++ 抽象类)"]
SDK_IOS["内部 SDK / 外部 .framework<br/>如 CoreBluetooth / Stripe"]
UIVIEW["UIView 子类<br/>(自定义 UI 组件)"]
end
subgraph NATIVE_AND["Android 原生实现"]
KOTLIN["Kotlin / Java 实现类<br/>(继承 C++ 抽象类)"]
SDK_AND["内部 SDK / 外部 .aar<br/>如 BluetoothGatt / Stripe"]
ANDROIDVIEW["View 子类<br/>(自定义 UI 组件)"]
end
SPEC -->|"读取类型定义"| CODEGEN
GEN_CPP --> JSI
GEN_SHADOW --> FABRIC
APP -->|"同步 / 异步调用"| JSI
JSI --> SWIFT & KOTLIN
FABRIC --> UIVIEW & ANDROIDVIEW
SWIFT --> SDK_IOS
KOTLIN --> SDK_AND关键在于 Spec → Codegen → 原生实现这一单向依赖关系。在 JS 侧编写类型定义,Codegen 在构建时生成 C++ 抽象类,Swift / Kotlin 实现类只需继承即可,类型一致性由编译器保证,"君子协定"导致的运行时崩溃随之消失。
8. TurboModule 实现流程(以蓝牙为例)
注:以下代码是基于 React Native 官方 TurboModule / Codegen / Custom Events 指南的简化示例,并非直接引用官方示例,而是以蓝牙为题材重新构建。
① 编写 JS Spec
// src/specs/NativeBluetoothModule.ts
import type { TurboModule, CodegenTypes } from 'react-native';
import { TurboModuleRegistry } from 'react-native';
export type ScanResult = {
id: string;
name: string;
rssi: number;
};
export interface Spec extends TurboModule {
startScan(serviceUUIDs: string[]): void;
stopScan(): void;
connect(deviceId: string): Promise<boolean>;
readonly onDeviceFound: CodegenTypes.EventEmitter<ScanResult>;
}
export default TurboModuleRegistry.getEnforcing<Spec>('NativeBluetoothModule');
② Codegen 自动生成的内容
flowchart LR
SPEC["NativeBluetoothModule.ts<br/>(开发者编写)"]
subgraph AUTO["自动生成的代码"]
CPP["C++ glue code<br/>JSI / Codegen 生成物"]
IOS_H["iOS glue code<br/>Obj-C++ / header 生成物"]
AND_J["Android glue code<br/>JNI / Spec 类生成物"]
end
subgraph IMPL["开发者实现"]
SWIFT_IMPL["NativeBluetoothModule.swift<br/>实现生成的 Spec,调用 CoreBluetooth"]
KOTLIN_IMPL["NativeBluetoothModule.kt<br/>实现生成的 Spec,调用 BluetoothGatt"]
end
SPEC --> CPP
SPEC --> IOS_H
SPEC --> AND_J
CPP --> SWIFT_IMPL & KOTLIN_IMPL
IOS_H --> SWIFT_IMPL
AND_J --> KOTLIN_IMPL③ iOS 实现(Swift)
// ios/NativeBluetoothModule.swift
import CoreBluetooth
@objc(NativeBluetoothModule)
class NativeBluetoothModule: NativeBluetoothModuleSpec, CBCentralManagerDelegate {
private var centralManager: CBCentralManager!
override func startScan(_ serviceUUIDs: [String]) {
let uuids = serviceUUIDs.map { CBUUID(string: $0) }
centralManager.scanForPeripherals(withServices: uuids)
}
func centralManager(_ central: CBCentralManager,
didDiscover peripheral: CBPeripheral,
advertisementData: [String: Any], rssi: NSNumber) {
emitOnDeviceFound([
"id": peripheral.identifier.uuidString,
"name": peripheral.name ?? "",
"rssi": rssi
])
}
}
④ Android 实现(Kotlin)
// android/src/main/java/NativeBluetoothModule.kt
class NativeBluetoothModule(reactContext: ReactApplicationContext)
: NativeBluetoothModuleSpec(reactContext), BluetoothGatt.Callback {
private val adapter = BluetoothAdapter.getDefaultAdapter()
override fun startScan(serviceUUIDs: ReadableArray) {
val filters = serviceUUIDs.toArrayList().map { uuid ->
ScanFilter.Builder().setServiceUuid(ParcelUuid.fromString(uuid as String)).build()
}
adapter.bluetoothLeScanner.startScan(filters, ScanSettings.Builder().build(), scanCallback)
}
private val scanCallback = object : ScanCallback() {
override fun onScanResult(callbackType: Int, result: ScanResult) {
val params = Arguments.createMap().apply {
putString("id", result.device.address)
putString("name", result.device.name ?: "")
putInt("rssi", result.rssi)
}
emitOnDeviceFound(params)
}
}
}
9. Fabric Component 实现流程(自定义相机视图示例)
如果 TurboModule 负责逻辑,Fabric Component 则用于将原生 UI 嵌入 JSX。使用 OpenGL / Metal 进行自定义渲染,或直接复用现有原生应用中的 View 时需要用到它。
注:以下也是用于理解的最小示例。Codegen 的 import 路径和类型名会因 React Native 版本而异,实现时请参考所用版本的官方文档。
// src/specs/NativeCameraViewSpec.ts
import type { ViewProps } from 'react-native';
import codegenNativeComponent from 'react-native/Libraries/Utilities/codegenNativeComponent';
import type { DirectEventHandler, Float } from 'react-native/Libraries/Types/CodegenTypes';
type FrameCapturedEvent = Readonly<{
uri: string;
}>;
interface NativeProps extends ViewProps {
zoom?: Float; // Codegen 将 Float 转换为 CGFloat / float
torchEnabled?: boolean;
onFrameCaptured?: DirectEventHandler<FrameCapturedEvent>;
}
export default codegenNativeComponent<NativeProps>('CameraView');
Codegen 从这个 Spec 生成 ShadowNode、ComponentDescriptor、Props 的 C++ 定义。iOS 实现继承 RCTViewComponentView 的 CameraViewComponentView,Android 继承 ReactViewGroup。在 JS 侧只需 <CameraView zoom={2.0} torchEnabled /> 即可使用。
10. 集成内部 SDK / 外部 .framework 和 .aar
iOS:通过 CocoaPods 本地引用
# ios/Podfile
pod 'CompanyPaymentSDK', :path => '../vendor/ios/CompanyPaymentSDK'
# 或 xcframework 的情况
pod 'CompanyPaymentSDK', :podspec => '../vendor/ios/CompanyPaymentSDK.podspec'
添加到 Podfile 并运行 pod install 后,可以像普通 Swift 类一样通过 import CompanyPaymentSDK 使用,直接从 TurboModule 实现类中调用即可。
Android:本地 Maven / .aar 引用
// android/app/build.gradle
dependencies {
implementation files('../vendor/android/company-payment-sdk.aar')
// 或内部 Maven 仓库
implementation 'com.company:payment-sdk:1.4.0'
}
11. 实际开发循环:从启动到验证
flowchart TD
INIT["npx create-expo-app MyApp<br/>--template blank-typescript"]
subgraph START["npx expo start"]
METRO["Metro Bundler 启动<br/>port 8081"]
QR["显示二维码 + 快捷键<br/>i = iOS a = Android w = Web"]
end
subgraph DEVICE["选择目标设备"]
SIM["iOS Simulator<br/>Xcode 自带(仅 Mac)"]
EMU["Android Emulator<br/>Android Studio AVD"]
GO["真机 + Expo Go<br/>局域网或 Tunnel"]
end
subgraph NATIVE_BUILD["仅首次:原生构建"]
POD["pod install<br/>iOS CocoaPods"]
GRADLE["Gradle build<br/>Android"]
CODEGEN_RUN["Codegen 执行<br/>Spec → 原生模板"]
end
BUNDLE["JS Bundle 传输到设备<br/>通过 Metro HTTP 服务器"]
RENDER["画面显示"]
INIT --> START
METRO --> QR
QR --> SIM & EMU & GO
SIM --> NATIVE_BUILD
EMU --> NATIVE_BUILD
NATIVE_BUILD --> BUNDLE
GO --> BUNDLE
BUNDLE --> RENDER运行 npx expo start 后 Metro 启动,终端显示二维码和键盘快捷键。
i键 → iOS Simulator(在安装了 Xcode 的 Mac 上运行)a键 → Android Emulator(在 Android Studio AVD Manager 中创建的虚拟设备)- 真机可用 Expo Go 扫描二维码,局域网内几乎瞬间连接
使用 npx expo run:ios 或 npx expo run:android 明确触发原生构建时,首次会执行 pod install(iOS)或 Gradle(Android),其中 Codegen 也会运行并生成原生模板代码。第二次起只传输 JS Bundle 差异,速度较快。
12. Bare Workflow 的变更循环
flowchart TD
subgraph CHANGE["按变更类型分支"]
JS_ONLY["仅 JS 变更<br/>组件 / 逻辑 / 样式"]
NATIVE_CHANGE["原生代码变更<br/>Swift / Kotlin / Spec / Podfile"]
end
subgraph JS_LOOP["JS 变更循环(快速)"]
METRO_HMR["Metro HMR<br/>传输差异 Bundle<br/>数百毫秒"]
SIM_JS["模拟器即时反映<br/>保留应用状态"]
end
subgraph NATIVE_LOOP["原生变更循环(慢)"]
CODEGEN_RUN["重新执行 Codegen<br/>pod install (iOS)<br/>Gradle sync (Android)"]
REBUILD["原生重新构建<br/>npx expo run:ios<br/>npx expo run:android"]
SIM_NATIVE["在模拟器 / 真机上确认"]
end
JS_ONLY --> METRO_HMR --> SIM_JS
NATIVE_CHANGE --> CODEGEN_RUN --> REBUILD --> SIM_NATIVE
SIM_JS -->|"没有问题"| DONE["验证完成"]
SIM_NATIVE -->|"没有问题"| DONE
SIM_JS -->|"发现 Bug"| DEBUG_JS["Hermes Debugger<br/>React DevTools"]
SIM_NATIVE -->|"原生崩溃"| DEBUG_NATIVE["Xcode / Android Studio<br/>原生调试器"]
DEBUG_JS --> JS_ONLY
DEBUG_NATIVE --> NATIVE_CHANGE仅 JS 变更时,Metro HMR 在数百毫秒内传输差异,状态也得以保留。
原生代码发生变更时需要重新构建。iOS 首次构建约需 2〜5 分钟,差异构建约 30 秒〜1 分钟。Android 在 Gradle 缓存生效的情况下类似,首次构建耗时更长。
这种不对称性是 Bare Workflow 开发效率的关键。预先确定原生边界(Spec),将大部分实现放在 JS 侧,可以降低频繁触发重量级原生构建的频率。
13. 调试:跨越 JS 与原生边界的问题排查
flowchart TD
SYMPTOM["观察症状"]
SYMPTOM --> Q1{"崩溃类型"}
Q1 -->|"JS 错误<br/>LogBox 红屏"| JS_ERR["用 Hermes Debugger 调查<br/>断点、堆栈跟踪<br/>Source Map 显示 TS 行号"]
Q1 -->|"原生崩溃<br/>应用崩溃 / 冻结"| NAT_ERR["检查崩溃日志"]
Q1 -->|"能运行但<br/>结果不正确"| BORDER["怀疑 JS↔Native 边界"]
NAT_ERR --> IOS_CRASH["iOS: Xcode<br/>Debug Navigator<br/>+ lldb 附加"]
NAT_ERR --> AND_CRASH["Android: Android Studio<br/>Logcat 堆栈跟踪<br/>+ Native Debugger"]
BORDER --> LOG_IOS["iOS: NSLog / os_log<br/>npx react-native log-ios"]
BORDER --> LOG_AND["Android: Log.d<br/>npx react-native log-android"]
BORDER --> JS_LOG["JS 侧: console.log<br/>确认 TurboModule 返回值"]
IOS_CRASH --> INSTRUMENTS["Instruments<br/>Time Profiler / Allocations<br/>定位内存泄漏 / CPU"]
AND_CRASH --> PROFILER["Android Studio<br/>Memory / CPU Profiler"]
JS_ERR --> FIXED["修复 → Metro HMR 即时反映"]
INSTRUMENTS --> FIXED2["修复 → npx expo run:ios 确认"]
PROFILER --> FIXED3["修复 → npx expo run:android 确认"]
LOG_IOS & LOG_AND & JS_LOG --> BORDER2["定位根本原因<br/>→ 修复 Spec / 实现"]JS 错误:Hermes Debugger
Cmd+D → "Open Debugger" 打开 Chrome DevTools。Metro 提供 Source Map,可以在原始 TypeScript 行号而非打包后的代码上设置断点,TurboModule 调用中发生的异常也能追踪到 JS 侧的调用堆栈。
原生崩溃:附加 Xcode / Android Studio 调试器
# iOS: 将 lldb 附加到运行中的模拟器
npx expo run:ios --configuration Debug
# → Xcode 自动打开并附加调试器
# Android
npx expo run:android
# → "Run > Attach Debugger to Android Process" 附加
对于只在真机上复现的崩溃(蓝牙、相机、传感器类尤其如此),可以用同样的方式将调试器附加到通过 USB 连接的真机上。
边界调查:用日志夹击
当 JS↔Native 边界的数据出现异常时,从两侧用日志夹击是最快的方法。
// Swift 侧
os_log("BluetoothModule.connect called: %{public}@", deviceId)
// → 用 npx react-native log-ios 过滤查看
// Kotlin 侧
Log.d("BluetoothModule", "connect called: $deviceId")
// → 用 npx react-native log-android 过滤查看
// JS 侧
const result = await BluetoothModule.connect(deviceId);
console.log('connect result:', result); // 输出到 Metro
React DevTools
npx react-devtools
# → 在端口 8097 等待设备连接
可视化组件树,直接在检查器中修改 props 和 state,实时确认效果。
14. 模拟器与真机的使用区分
| 验证内容 | iOS Simulator | Android Emulator | 真机 |
|---|---|---|---|
| UI / 布局 | 足够 | 足够 | 最终确认 |
| JS 逻辑 / API | 足够 | 足够 | 不需要 |
| 蓝牙 / NFC | 不可用 | 不可用 | 必须 |
| 相机 / 麦克风 | 部分可用 | 不可用 | 必须 |
| 推送通知 | 部分可用 | 部分可用 | 推荐 |
| 性能 | 仅供参考 | 仅供参考 | 必须 |
| 生物识别 | 可模拟 Face ID | 可模拟指纹 | 推荐 |
涉及原生模块的开发,实用的两阶段方案是:在模拟器上固化 JS 行为,在真机上验证原生功能。蓝牙和 NFC 完全无法在模拟器上测试,因此真机数量和 OS 版本覆盖率直接影响质量。
总结
技术栈全貌
| 层次 | 技术 | 作用 |
|---|---|---|
| 打包器 | Metro | 源代码转译、HMR、平台分支 |
| JS 引擎 | Hermes | 字节码执行以加速启动 |
| JS↔Native 桥接 | JSI | 通过 C++ 的同步绑定 |
| 代码生成 | Codegen | 从 Spec 自动生成类型安全的原生模板 |
| UI 渲染器 | Fabric | 基于 JSI 的同步 UI 渲染 |
| 原生 API | TurboModules | 继承 Codegen 生成类的懒加载模块 |
| 开发基础设施 | Expo / EAS | SDK、云端构建、OTA 更新 |
涉及原生实现的开发全流程
① 编写 JS Spec(TypeScript 类型定义)
↓
② pod install / gradle build → Codegen 自动生成 C++ 模板
↓
③ 用 Swift / Kotlin 实现(调用内部 SDK / 外部库)
↓
④ npx expo run:ios / run:android 原生构建 & 模拟器验证
↓
⑤ 编写 JS 调用代码 → 通过 Metro HMR 快速迭代
↓
⑥ JS 错误 → Hermes Debugger
原生崩溃 → Xcode / Android Studio 调试器
边界不一致 → 用 log-ios / log-android + console.log 夹击
↓
⑦ 在真机上验证蓝牙 / 相机 / 传感器等功能
↓
⑧ EAS Build 生成应用商店发布的二进制文件
从 Bridge 迁移到 JSI + Codegen,React Native 完成了从"能跑但脆弱"到"类型安全、高性能"的蜕变。迈出 Expo 的范围会带来构建耗时增加、两种语言编写相同逻辑等成本,但 Codegen 让 Spec 成为唯一的真实来源,在设计阶段就消除了由类型不匹配导致运行时崩溃这一最大风险。
参考链接
注:本文基于以下资料进行了总结和重构。文中说明和代码示例为便于理解做了简化,并非直接引用。实际实现时请优先参考所用版本的官方文档。
参考 URL
React Native 官方
- React Native 新架构(概述): https://reactnative.dev/architecture/landing-page
- JSI 和 TurboModules 概述: https://reactnative.dev/docs/turbo-native-modules-introduction
- Codegen 是什么: https://reactnative.dev/docs/the-new-architecture/what-is-codegen
- 如何使用 Codegen: https://reactnative.dev/docs/the-new-architecture/using-codegen
- 从 TurboModule 发送事件: https://reactnative.dev/docs/the-new-architecture/native-modules-custom-events
- Codegen 类型定义附录: https://reactnative.dev/docs/appendix
- Hermes 引擎: https://reactnative.dev/docs/hermes
- Metro(React Native 官方): https://reactnative.dev/docs/metro
Expo 官方
- 为何使用 Metro(Expo): https://docs.expo.dev/guides/why-metro/
- 使用 Hermes(Expo): https://docs.expo.dev/guides/using-hermes/
- EAS 概述: https://docs.expo.dev/eas/
- EAS Build 概述: https://docs.expo.dev/build/introduction/
- EAS Update 概述: https://docs.expo.dev/eas-update/introduction/
