React Native架构与源码深度解读
# React Native 架构与源码深度解读
React Native(以下简称 RN)凭借“一次编写,多端运行”的理念,迅速在移动开发领域崛起。本篇文章将从整体架构层面入手,逐步深入其底层源码,包括 JS 与原生桥接机制、渲染双引擎、Fabric 架构、TurboModules、架构演变和示例代码解析。文章结构如下:
1. [前言:为什么要深度解读 React Native](#前言为什么要深度解读-react-native)
2. [整体架构概览](#整体架构概览)
1. [JavaScript 线程与原生线程分离](#javascript-线程与原生线程分离)
2. [桥接(Bridge)机制](#桥接bridge机制)
3. [UI 管道:从 JS 到原生视图](#ui-管道从-js-到原生视图)
3. [旧架构:Bridge+ShadowTree 渲染流程](#旧架构bridgeshadowtree-渲染流程)
1. [JavaScriptCore 与 Metro Bundler](#javascriptcore-与-metro-bundler)
2. [Shadow Tree(阴影树)与布局计算](#shadow-tree阴影树与布局计算)
3. [Bridge 调用与异步队列](#bridge-调用与异步队列)
4. [示例:自定义 Native Module](#示例自定义-native-module)
4. [新架构:Fabric & TurboModules](#新架构fabric--turbomodules)
1. [为何引入 Fabric?旧架构不足点](#为何引入-fabric旧架构不足点)
2. [Fabric 渲染管线核心](#fabric-渲染管线核心)
3. [TurboModules:更高效的原生模块访问](#turbomodules更高效的原生模块访问)
4. [示例:TurboModule 定义与调用](#示例turbomodule-定义与调用)
5. [架构图解与流程示意](#架构图解与流程示意)
6. [源码剖析重点文件与目录](#源码剖析重点文件与目录)
1. [`ReactAndroid/` 目录结构](#reactandroid-目录结构)
2. [`ReactCommon/` 目录要点](#reactcommon-目录要点)
3. [`React/RCTModule/` 与 `Fabric/`](#reactrctmodule-与-fabric)
7. [架构演进与性能优化点](#架构演进与性能优化点)
1. [从 “Bridge-only” 到 “Turbo” 路线图](#从-bridge-only-到-turbo-路线图)
2. [JSC → Hermes 引擎切换](#jsc--hermes-引擎切换)
3. [Concurrent React 兼容与异步更新](#concurrent-react-兼容与异步更新)
8. [React Native 常见性能调优实践](#react-native-常见性能调优实践)
9. [总结与学习建议](#总结与学习建议)
---
## 前言:为什么要深度解读 React Native
对 RN 源码进行深度研究,可以帮助我们:
1. **理解性能瓶颈来源**:了解 JS 和原生交互、渲染流程,有助于定位性能热点。
2. **自定义 Native 组件**:知道桥接如何工作,才能正确编写高性能的自定义原生模块和视图组件。
3. **紧跟架构演进**:Fabric、TurboModules、Hermes 和 Concurrent React 都在不断演进,深入源码才能快速上手并解决兼容问题。
4. **提高开发效率**:借鉴 RN 架构设计思想,优化自己的项目结构和工程化方案。
下面逐层剖析 RN 底层管理 JS、原生模块与 UI 渲染的关键技术细节,帮助你迅速理清全局脉络。
---
## 整体架构概览
React Native 将移动端应用分为两部分:**JavaScript 线程** 与 **原生线程**(UI 线程 + Java/Kotlin、Objective-C/Swift 层)。它们通过桥接(Bridge)进行异步通信。整体架构可分为三大模块:
1. **JS 引擎与逻辑执行**
- 内置 JavaScriptCore(iOS)或使用 Hermes(Android/iOS),执行用 React 语法编写的业务逻辑与 UI 声明。
- `Metro Bundler` 将所有 JS 模块打包成单一 `index.bundle`(或多个分块 bundle),供手机端加载。
2. **桥接(Bridge)层**
- 负责在 JS 线程与原生线程之间进行消息编解码。旧架构使用 JSON 序列化,成为性能瓶颈;新架构下的 TurboModules 和 JSI(JavaScript Interface) 通过共享 C++ 对象指针,大大降低了往返开销。
3. **UI 管道**
- **旧架构**:JS 计算出最终的 Shadow Node 树(阴影树),并通过 Bridge 把变更下发到原生 Shadow Tree 模块,由 Yoga 计算布局,生成具体坐标后再下发通过 UIManager 进行原生 View 的增删改操作。
- **新架构 Fabric**:直接在 C++ 层面管理“Shadow Tree”并使用 JSI 直接回调布局与渲染接口,整个流程更底层、更细粒度、更高效。
下图简要展示两种架构的对比流程:
旧架构(Bridge + Shadow Tree) 新架构(Fabric + TurboModules)
╔════════════════════════════╗ ╔════════════════════════════╗
║ JS 线程(JSC/Hermes) ║ ║ JS 线程(JSC/Hermes) ║
║ React 组件与业务逻辑 ║ ║ React 组件与业务逻辑 ║
╠════════════════════════════╣ ╠════════════════════════════╣
║ Shadow Props → JSX 元素 ║ ║ JSX 元素 → JSI 直接调用 C++ ║
║ (虚拟 DOM 树) ║ ║ Fabric Shadow Node 构建 ║
╠════════════════════════════╣ ╠════════════════════════════╣
║ Bridge(JSON 序列化) ║◀─────────▶ ║ JSI(Host Object / Host ║
║ 发送更新指令到 Native ║ ║ Function) ║
╠════════════════════════════╣ ╠════════════════════════════╣
║ Native 阴影树(Yoga 计算) ║ ║ C++ 阴影树(Yoga/新版布局) ║
║ 计算最终布局 → UIManager ║ ║ 直接在 C++ 执行布局与渲染 ║
╠════════════════════════════╣ ╠════════════════════════════╣
║ UI 线程:原生 View 操作 ║ ║ UI 线程:原生 View 操作 ║
╚════════════════════════════╝ ╚════════════════════════════╝
下面我们分章节详细说明各个模块的核心工作原理与源码实现思路。
---
## 旧架构:Bridge+ShadowTree 渲染流程
在 RN 0.60 以前,核心架构主要依赖桥接(Bridge)和 Yoga 布局(由 Facebook 开源)。这一套架构虽已逐渐被 Fabric 取代,但理解它对后续学习 Fabric 和 TurboModules 非常重要。
### JavaScriptCore 与 Metro Bundler
1. **JavaScriptCore(JSC)**
- RN 默认在 iOS 平台使用 iOS 系统自带的 JSC;在 Android 端原先也依赖社区提供的 JSC 库,后来可以选择使用 Hermes。
- JSC 会在 App 启动时将 `main.jsbundle` 加载到内存,启动一个 JS 线程。此线程执行 React 业务逻辑、Hooks、状态更新和渲染计算。
2. **Metro Bundler**
- `Metro` 是 RN 的打包工具,会把各个 JS 模块打包成一个(或多个)可供原生端加载的 bundle。例如:`index.android.bundle`、`index.ios.bundle`。
- 支持 source maps,以便调试。
3. **JS 线程入口**
- 在 iOS `AppDelegate.m` 的 `didFinishLaunching` 中,会调用:
```objc
RCTBridge *bridge = [[RCTBridge alloc] initWithDelegate:self launchOptions:launchOptions];
RCTRootView *rootView = [[RCTRootView alloc] initWithBridge:bridge moduleName:@"YourAppName" initialProperties:nil];
```
- `RCTBridge` 会加载本地或远程 `main.jsbundle`,创建一个 `JSContext`,并在其中执行 `AppRegistry.registerComponent('YourAppName', () => App);` 注册根组件。
### Shadow Tree(阴影树)与布局计算
在旧架构中,RN 维护了两棵树:**Shadow Tree(ShadowNode)** 和真实 **View Tree(UIView/Android View)**。Shadow Tree 负责布局计算和离屏差分,最终再将变化下发真实 View 对象。
1. **ShadowNode**
- 对应 RN 中每个 JS 组件的“原生阴影节点”,定义在 `ReactCommon/yoga` 或 `React/CoreModules/RCTUIManager` 中。ShadowNode 只存储布局属性(如 `flex`、`margin`、`padding`、`width`、`height`)。
- ShadowNode 树的构建基于 JS 端调用 `React.createElement` 产生的虚拟 DOM,当 JS 调用 `setState` 或 `props` 发生变化时,新的 ShadowNode 会重新进入布局计算。
2. **Yoga 布局引擎**
- Facebook 开源的跨平台布局库(基于 FlexBox 算法),通过对 ShadowNode 树执行 `YGNodeCalculateLayout`,得到每个节点最终的 `x, y, width, height`。
- ShadowNode 的属性会同步设置到 Yoga 中间层,为布局计算提供依据。
3. **Bridge 下发 UI 操作**
- 当 ShadowNode 布局计算完成后,RN 会遍历 Shadow Tree,对比新旧布局差异,将每个需要移动、插入或删除的 ShadowNode 生成一条 “UI 编辑指令(UIOperation)” → 通过 Bridge 发送给原生端的 UIManager。
- UIManager 将这些指令转换为对应的 `UIView`(iOS)或 `ViewGroup`(Android)操作,例如 `createView`、`updateView`、`manageChildren`。
4. **示例:ShadowNode 更新流程**
- JS 端调用 `setState` → 触发 `RCTExecuteOnJavaScriptQueue` 执行组件 render → 新旧虚拟 DOM 差分 → 调用 `UIManager.createView` / `UIManager.updateView` → ShadowNode 属性更新 → Yoga 重新计算 → 生成布局结果 → UIManager 下发真实 View 更新 → 原生 UI 线程执行相应操作。
### Bridge 调用与异步队列
桥接层将 JS 与原生分离,通过异步消息队列通信。核心实现 resides 于:
- **iOS:`RCTBridge.mm` 与 `RCTBatchedBridge`**
- **Android:`CatalystInstanceImpl.java` **
1. **信息编码**
- 旧架构将需要传递的参数(如创建 View 时的属性字典)序列化为 JSON 数组,再通过 JNI(Android)或 Objective-C 原生函数(iOS)发送给对端。
- 此种方式的缺点在于 JSON 序列化开销大、内存分配频繁、跨线程上下文切换耗时。
2. **异步队列**
- JS 线程将所有桥接请求(例如 `UIManager.createView(...)`、`NativeModules.YourModule.yourMethod(...)`)推入一个被称为 “Batched Bridge Queue” 的队列,定期打包成一批消息下发给原生。
- 原生执行完成后,可选择通过回调将结果再回传给 JS。
3. **示例:调用 Native Module**
- JS 端代码:
```js
import { NativeModules } from 'react-native';
NativeModules.ToastModule.show('Hello from JS!', NativeModules.ToastModule.SHORT);
```
- JS 线程将这条调用封装成 `["ToastModule", "show", ["Hello from JS!", 0]]`,放入 BatchedBridge 队列。
- 原生端收到 JSON 消息后,在相应 Module 中找到 `show` 方法并执行。
### 示例:自定义 Native Module
下面演示如何在旧架构下为 Android 创建一个简单的 Toast Module。
1. **创建 Java 类 `ToastModule.java`**
```java
// android/app/src/main/java/com/yourapp/ToastModule.java
package com.yourapp;
import android.widget.Toast;
import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.bridge.ReactContextBaseJavaModule;
import com.facebook.react.bridge.ReactMethod;
public class ToastModule extends ReactContextBaseJavaModule {
private static ReactApplicationContext reactContext;
public ToastModule(ReactApplicationContext context) {
super(context);
reactContext = context;
}
@Override
public String getName() {
return "ToastModule";
}
@ReactMethod
public void show(String message, int duration) {
Toast.makeText(reactContext, message, duration).show();
}
}
创建
ToastPackage.java
// android/app/src/main/java/com/yourapp/ToastPackage.java package com.yourapp; import com.facebook.react.ReactPackage; import com.facebook.react.bridge.NativeModule; import com.facebook.react.bridge.ReactApplicationContext; import com.facebook.react.uimanager.ViewManager; import java.util.ArrayList; import java.util.Collections; import java.util.List; public class ToastPackage implements ReactPackage { @Override public List<NativeModule> createNativeModules(ReactApplicationContext reactContext) { List<NativeModule> modules = new ArrayList<>(); modules.add(new ToastModule(reactContext)); return modules; } @Override public List<ViewManager> createViewManagers(ReactApplicationContext reactContext) { return Collections.emptyList(); } }
注册到
MainApplication.java
// android/app/src/main/java/com/yourapp/MainApplication.java @Override protected List<ReactPackage> getPackages() { List<ReactPackage> packages = new PackageList(this).getPackages(); // 手动添加 packages.add(new ToastPackage()); return packages; }
JS 端调用示例
// App.js import React from 'react'; import { Button, NativeModules, View } from 'react-native'; export default function App() { const { ToastModule } = NativeModules; return ( <View style={{ flex: 1, justifyContent: 'center', alignItems: 'center' }}> <Button title="Show Toast" onPress={() => ToastModule.show('Hello from RN!', ToastModule.SHORT)} /> </View> ); }
这样,当你按下按钮时,JS 端通过 Bridge 将调用下发到原生,执行 Toast.makeText
显示 Android 系统 Toast。
新架构:Fabric & TurboModules
为了解决 Bridge 性能瓶颈与布局卡顿问题,React Native 社区推出了新的架构方案:Fabric 渲染管线 和 TurboModules。自 RN 0.62 起分步引入,RN 0.65+ 已支持并逐渐默认启用。
为何引入 Fabric?旧架构不足点
Bridge 性能瓶颈
- JSON 序列化与反序列化开销大,往返耗时明显。
- Shadow Tree → Layout → UI 操作需要多次跨桥,UI 更新延迟。
无法细粒度调度布局
- 旧架构 ShadowNode 计算与 UI 更新合并在一起,难以“中断”或“并发处理”子树,更难适配 Concurrent React。
原生模块加载不够灵活
- NativeModules 以“静态注册”为主,不支持按需加载,高数量原生模块时耗时且占内存。
Fabric 渲染管线核心
Fabric 架构在 C++ 层面实现了 Shadow Tree,核心思想是将布局与渲染管线下沉到 C++,并借助 JSI(JavaScript Interface)将它暴露给 JS,从而使 JS 与原生能在同一个事件循环中交互,无需通过异步 Bridge。
JSI(JavaScript Interface)
- JSI 是一套在 JS 引擎(JSC 或 Hermes)与 C++ 之间的通信接口。与旧 Bridge 不同,JSI 直接将 C++ 对象注入到 JS 全局变量中,避免 JSON 序列化/反序列化。
FabricUIManager
、TurboModuleManager
等都通过 JSI 注册为 JS 可调用的对象(Host Object / Host Function)。
Fabric Shadow Tree
- ShadowNode 转移到 C++,并使用 Yoga(或重写的 C++ 布局引擎)计算布局。
- JS 通过 JSI 直接调用
FabricUIManager.createNode(tag, props, rootTag)
创建 ShadowNode; - 当组件更新时,JS 端调用
FabricUIManager.updateNode(node, newProps)
,C++ 端更新布局属性并触发重排。
同步调用与并发
- Fabric 支持 JS 与原生在同一线程进行布局与渲染交互,可灵活支持 Concurrent React。
- 在 C++ 层使用“Reconcile on UI Thread”模式,让 UI 更新更流畅,减少卡顿。
示意流程
JS 线程(Hermes/JSC) C++ 层(Fabric) 原生 UI 线程 ┌─────────────────────────────┐ ┌────────────────────┐ │ React render() → JSX 树 │ │ │ │ createNode(hostType, props) │ ──┐ │ │ └─────────────────────────────┘ │ │ │ │ │ Fabric Shadow Tree │ ┌─────────────────────────────┐ │ │ layoutWithYoga() │ │ updateProps(node, newProps) │ ──┼──▶│ 计算 x,y,width,height │ └─────────────────────────────┘ │ └────────────────────┘ │ │ │ ▼ │ ┌────────────────────┐ │ │ UI Commands List │ │ │(e.g. createView/ │ │ │ updateView/ remove)│ │ └────────────────────┘ │ │ │ ▼ JS 可并发调度(Concurrent Mode) │ ┌────────────────────┐ └─────────────────────────────┘ │ 原生 UI 线程 │ │ apply UI Commands │ └────────────────────┘
TurboModules:更高效的原生模块访问
TurboModules 架构将旧的 NativeModules
静态注册替换为按需加载的模块系统,主要特点如下:
JSI 桥接
- 通过 JSI 将每个原生模块(C++ / Java / Objective-C)注册为一个 Host Object,在 JS 端直接读取,这种方式无须异步 Bridge,调用更快。
按需加载
- 模块仅在首次调用时加载,大大减少冷启动时的开销。
- 不再需要在
MainApplication.java
中手动注册所有模块,而是遵循约定目录ReactCommon/turbomodule/
或 Gradle 配置自动生成。
异步与同步方法支持
- TurboModules 支持直接返回同步值(如获取设备信息)或使用 Promise(异步)。
- 通过 JSI 将回调和 Promise 直接挂到 JS 引擎上,无需 JSON 序列化。
示例:定义一个简单的 TurboModule
以下以 iOS 为例,演示在 new architecture 下创建一个简单的DeviceInfo
TurboModule。Objective-C++ 接口声明
// ios/ReactNativeNewArch/DeviceInfoModule.h #import <React/RCTBridgeModule.h> #import <ReactCommon/RCTTurboModule.h> #import <React/RCTMethodInfo.h> @interface DeviceInfoModule : NSObject <RCTTurboModule> @end
实现
.mm
文件// ios/ReactNativeNewArch/DeviceInfoModule.mm #import "DeviceInfoModule.h" #import <UIKit/UIKit.h> #import <jsi/jsi.h> #import <React/RCTJSIUtils.h> using namespace facebook::jsi; @implementation DeviceInfoModule RCT_EXPORT_MODULE(DeviceInfo); // 在 Fabric+TurboModules 中,推荐使用 JSINativeModules 方式 - (std::shared_ptr<facebook::react::CallInvoker>)getCallInvoker { return RCTCxxBridge_getJSCallInvoker(_bridge.compilerFlags); } - (void)install:(facebook::jsi::Runtime &)runtime { auto deviceInfo = Object::create(runtime); auto getSystemVersion = Function::createFromHostFunction( runtime, PropNameID::forAscii(runtime, "getSystemVersion"), 0, [](Runtime &rt, Value const &, Value const *args, size_t) -> Value { NSString *version = [UIDevice currentDevice].systemVersion; std::string ver = [version UTF8String]; return String::createFromUtf8(rt, ver); } ); deviceInfo.setProperty(runtime, "getSystemVersion", getSystemVersion); runtime.global().setProperty(runtime, "DeviceInfo", deviceInfo); } @end
JS 端调用示例
// 在 DevTools 中调试 console.log(DeviceInfo.getSystemVersion()); // e.g. "14.4"
这段代码利用 JSI 将
DeviceInfo.getSystemVersion()
直接挂载到 JS 全局,通过 C++/Objective-C++ 获取系统版本并返回,无需走 Bridge。
架构图解与流程示意
下面汇总一个较为完整的架构图,帮助你理清旧架构与新架构中各个模块的职责与调用链路。图中以箭头表示调用方向、数据流转路径。
┌─────────────────────────────────────────────────────────────────────────┐
│ JS 线程(JSC / Hermes) │
│ │
│ ┌─────────────────────────────────────────────────────────────────┐ │
│ │ React 应用层 │ │
│ │ App.js → 组件树 → Hooks / Redux / 状态管理 → JSX 构建虚拟 DOM │ │
│ └─────────────────────────────────────────────────────────────────┘ │
│ │ │ │ │ │
│ ▼ ▼ ▼ ▼ │
│ ┌──────────────────┐ ┌────────┐ ┌────────────┐ ┌─────────┐ │
│ │ setState / dispatch │ │ navigate │ │ AsyncStorage │ │网络请求等│ │
│ └──────────────────┘ └────────┘ └────────────┘ └─────────┘ │
│ │ │
│ ▼ │
│ ┌─────────────────────────────────────────────────────────────────┐ │
│ │ Fabric 管线 / 旧 Bridge │ │
│ │ ┌──────────────┐ ┌───────────────────┐ ┌─────────────────────┐ │ │
│ │ │ TurboModule │ │ Bridge / JSI │ │ UIManager / FabricUI │ │ │
│ │ │ (按需加载) │ │ (JSON / HostObject)│ │ Shadow Nodes │ │ │
│ │ └──────────────┘ └───────────────────┘ └─────────────────────┘ │ │
│ │ ▲ ▲ ▲ │ │
│ │ │ │ │ │ │
│ │ │ (方法调用) │ (事件 / 更新) │ (布局 / 渲染) │ │
│ │ │ │ │ │ │
│ └───────┴─────────────────┴──────────────────────┴───────────────┘ │
│ │ │ │ │
│ ▼ ▼ ▼ │
│ ┌──────────────────┐ ┌──────────────────┐ ┌──────────────────┐ │
│ │ Native Modules │ │ Shadow Tree / │ │ Layout Engine │ │
│ │ (Java / Obj-C) │ │ Fabric C++ 节点 │ │ (Yoga or C++ )│ │
│ └──────────────────┘ └──────────────────┘ └──────────────────┘ │
│ │ │ │ │
│ ▼ ▼ ▼ │
│ ┌─────────────────────────────────────────────────────────────────┐ │
│ │ 原生 UI 线程(iOS/Android) │ │
│ │ ┌───────────────────────────────────────────────────────────┐ │ │
│ │ │ 原生 View Hierarchy / Layer │ │ │
│ │ │ createView / updateView / manageChildren / removeView │ │ │
│ │ └───────────────────────────────────────────────────────────┘ │ │
│ │ ▲ ▲ │ │
│ │ │ │ │ │
│ │ │ 用户触摸事件 / 原生回调 │ Native Event 回传 │ │
│ │ │ │ │ │
│ └─────────┴──────────────────────────────┴────────────────────────┘ │
└─────────────────────────────────────────────────────────────────────────┘
- JS 线程:负责业务逻辑、Hooks、组件树、状态更新、构建 ShadowNode / Fabric 指令,并通过 Bridge/JSI 与原生交互。
- Fabric 管线 / 旧 Bridge:连接 JS 与原生。Fabric 下移 ShadowNode 到 C++,并借助 JSI 直接回调,Bridge 下旧有 JSON 串行化方式。
- Native 线程:管理原生 View 层级,执行增删改操作,并将触摸等事件回传给 JS(例如
TouchableOpacity
的onPress
)。
源码剖析重点文件与目录
RN 源码庞大,但对理解架构极为重要的目录集中在以下位置:
ReactAndroid/
目录结构
ReactAndroid/src/main/java/com/facebook/react/
CatalystInstanceImpl.java
:旧架构下 Bridge 核心实现,负责注册 Module、MessageQueue、调用 JS。UIManagerModule.java
:管理 ShadowNode、布局计算、最终下发 View 操作给原生。ReactRootView.java
:RN 根视图容器,加载 JS Bundle 并初始化 Bridge。Fabric
子目录:Fabric 架构相关 Android 实现,包括FabricUIManager.java
、MountingManager.java
。TurboModule
子目录:TurboModules 相关工厂和管理器,例如TurboModuleManager.java
。
ReactAndroid/src/main/jni/
- 包含 C++ 层面对 JSI、Fabric、Yoga 引擎的绑定。
ReactCommon/
目录要点
jsi/
- JSI 数据类型与接口定义,例如
jsi.h
、Value.h
、Runtime.h
。
- JSI 数据类型与接口定义,例如
jsiexecutor/
- JSI 在 Android 与 iOS 上的桥接实现,例如
JSCRuntime
、HermesRuntime
。
- JSI 在 Android 与 iOS 上的桥接实现,例如
fabric/
- Fabric C++ 核心代码,包含 ShadowNode 定义、事件调度、UIManager 接口。
turbomodule/
- 定义了 TurboModule ABI,C++ / Java / Obj-C++ 通过这个接口生成对应的模块绑定。
yoga/
- Facebook 开源的 Yoga 布局引擎源代码。
React/RCTModule/
与 Fabric/
React/RCTModule/
- 旧版 NativeModule 注册与调用机制,包含
RCTBridgeModule.h
、RCTBridge.h
。
- 旧版 NativeModule 注册与调用机制,包含
Fabric/
- iOS 端的 Fabric 相关目录,包含
RCTFabricUITurboModuleManager.mm
、RCTSurfacePresenter
等。
- iOS 端的 Fabric 相关目录,包含
以上目录和文件是 RN 架构的核心,深入阅读这些源码可直接理解 RN 如何从 JS 调用原生、如何执行布局、如何同步渲染。
架构演进与性能优化点
从 “Bridge-only” 到 “Turbo” 路线图
Bridge-only 架构(RN 0.59 及以前)
- JSON 串行化 → native 执行 → JSON 反序列化,异步队列。
- ShadowTree → Yoga 布局 → UIManager。
TurboModules + Fabric(RN 0.62 – 0.66)
- 使用 JSI 替代 JSON Bridge,NativeModules 改为 TurboModules,布局在 C++ 层面。
- JS 与 UIManager 直接在同一线程交互,可实现“同步渲染”与“部分异步渲染”。
Hermes 嵌入(RN 0.64 及以后)
- Android 默认可切换到 Hermes,性能更优,内存占用更低。
- JSC 下仍保留,但社区推荐使用 Hermes 以配合 Fabric / TurboModules。
Concurrent React 兼容(RN 0.68+)
- 支持 React 18 中的并发特性,Fiber reconciler 与 Scheduler 调度更紧密结合。
- Fabric 管线结合
startTransition
、useDeferredValue
等并发 API,提升复杂 UI 的渲染流畅度。
JSC → Hermes 引擎切换
JSC(JavaScriptCore)
- iOS 平台默认 JSC;Android 端曾经使用
jsc-android
二进制包。 - 缺点:启动慢、内存占用高、缺乏及时更新。
- iOS 平台默认 JSC;Android 端曾经使用
Hermes
- Facebook 自研的 JavaScript 引擎,针对 RN 做了裁剪,启动速度快、内存占用低。
RN 0.64+ 引入
hermes-engine
,可通过enableHermes: true
在android/app/build.gradle
中启用:project.ext.react = [ enableHermes: true, // must be true to use Hermes ]
- 相较 JSC,Hermes 在首包启动时间可提升约 25%\~40%,并在动画、JS 密集计算场景更流畅。
Concurrent React 兼容与异步更新
React 18 提出了“并发模式”,RN 也逐步支持:
- 在 JS 端编写组件时,可使用
startTransition(() => setState(...))
标记“可延迟更新”,允许 UI 在用户滚动、点击等操作中保持流畅。 - Fabric 通过底层的
Scheduler
配合JSI
调度,能在 UI 线程与 JS 线程之间分割任务,避免长时间占用导致卡顿。
- 在 JS 端编写组件时,可使用
React Native 常见性能调优实践
避免过度使用 Bridge
- 批量调用 Bridge 操作(例如
UIManager
),而非频繁单条调用。 - 旧架构下可使用
UIManager.dispatchViewManagerCommand
一次传多个命令。
- 批量调用 Bridge 操作(例如
使用 FlatList 优化长列表
FlatList
支持windowSize
、initialNumToRender
、maxToRenderPerBatch
等属性,通过分片渲染列表项减轻 JS+UI 线程压力。- 利用
getItemLayout
提前告知每行高度,避免测量造成的抖动。
减少重绘与动画优化
- 在大范围可动画控件上尽量启用
useNativeDriver: true
,将动画交给原生驱动,避免 JS 持续插针。 - 尽量减少在 render 中进行复杂计算,可用
useMemo
或useCallback
缓存。
- 在大范围可动画控件上尽量启用
避免匿名函数 & 重复创建对象
- 在 JSX 中避免为每次 render 创建匿名函数或对象(如
style={{...}}
),否则每次都会导致子组件重新渲染。 - 将常用回调或样式提升到外部或使用
useCallback
/useMemo
优化。
- 在 JSX 中避免为每次 render 创建匿名函数或对象(如
使用 Hermes 性能剖析工具
- Hermes 自带的堆栈采样工具可分析 JS 侧的性能热点。
- 借助
systrace
工具可更精准地剖析 UI 线程与 JS 线程之间的时间分配。
慎用
InteractionManager
- 当任务量较重时,用
InteractionManager.runAfterInteractions
推迟到交互后执行,但若任务过多仍可能造成卡顿,应酌情使用。
- 当任务量较重时,用
总结与学习建议
本文从 RN 整体架构入手,深入解读了旧架构 Bridge + Shadow Tree 以及全新的 Fabric + TurboModules 管线,结合代码示例与架构图解,帮助你快速理清 RN 底层逻辑与演进路径。要真正掌握 RN 源码,建议采取如下学习路径:
阅读官方文档与架构设计文档
- 官方博客经常发布架构演进文章。
- React Native 核心仓库中的
Architecture.md
、TurboModules.md
等文档非常有帮助。
从简单示例入手
- 先实现一个自定义 Native Module,再升级为 TurboModule。
- 逐步尝试手动创建 Fabric 节点并渲染。
调试与打断点
- 在模拟器 / 手机上运行 RN 应用时,在 Xcode 或 Android Studio 对
CatalystBridge
、FabricUIManager
等处打断点,观察 JS 与 Native 之间数据流转。
- 在模拟器 / 手机上运行 RN 应用时,在 Xcode 或 Android Studio 对
关注社区与 RFC
- React Native 社区和 React Core 团队会定期在 GitHub 上发布 RFC(Request for Comments),讨论即将到来的架构变动。
- 关注
react-native-website
中的 “Architecture” 栏目,及时了解新特性。
实践与迁移
- 如果你有现有项目,尝试启用 Fabric 与 TurboModules,观察代码中需要修改的地方。
- 在不同机型与平台上测试性能差异,积累实际经验。
通过持续的源码阅读与实践,相信你很快能深入理解 React Native 的底层原理,并能够在项目中定制高性能的原生组件或优化方案。
评论已关闭