React Native 运行时深度解析

当你第一次用React Native写完一个按钮,点击后看到原生界面乖乖响应时,可能会惊叹:"JavaScript居然能指挥原生控件?"这种魔法般的体验,让无数开发者爱上RN。但当你 profiling 应用时发现莫名其妙的白屏,或列表滚动掉帧时,又会困惑:"我的JS代码到底在哪执行?它和原生UI之间隔着什么?"这些问题的答案,都藏在RN运行时的三线程通信、Bridge序列化与JSI直接调用的细节里。本文将抛开JSX与样式表,带你深入C++层,看清每一帧渲染如何跨越语言边界,以及新架构如何让"JS写原生"真正飞起来。

一、核心架构概览

React Native 运行在三个主要线程 上:

二、旧架构:Bridge 机制(Legacy Architecture)

先讲一下旧架构,即Bridge机制

1. 工作原理

Bridge 是 React Native 0.68 之前 的核心,采用异步序列化通信

那何为异步序列化呢,以下有几个概念:

  • 异步性:所有调用都是异步的,无法同步返回复杂数据

  • 序列化开销:大数据量通信时性能瓶颈明显

  • 单通道:所有通信挤在一个队列,容易阻塞

三、新架构:JSI + Fabric + TurboModules

1. JSI (JavaScript Interface)

JSI 是 C++ 层,提供了直接持有原生对象引用的能力

javascript 复制代码
// C++ 侧暴露原生对象
class NativeModule {
public:
  void showToast(jsi::String message) {
    // 直接调用原生 API
  }
};

// JS 侧直接同步调用
const nativeModule = global.__turboModuleProxy('ToastAndroid');
nativeModule.showToast('Hello'); // 同步执行!

核心优势

  • 同步调用:支持同步返回复杂对象

  • 对象引用:JS 直接持有 C++ 对象指针,无需序列化

  • 动态绑定:运行时动态加载模块,启动更快

2. Fabric 渲染系统

Fabric 是新的 UI 渲染器,取代旧的 UI Manager

单说概念比较抽象,举两个例子:

旧渲染模型 :JS线程算完布局 → 写信 序列化 成 JSON → 寄到 Shadow 线程排好版 → 再寄到 UI 线程画出来。全程异步单通道,一步堵步步堵。

新渲染模型 :JS 通过 JSI 直接给 C++ 层打电话 → C++ 一边算布局一边指挥 UI 线程画。同步调用,两边同时干活,不绕弯子。

流程对比图

总感觉我举得栗子还是比较抽象,让AI帮我们想一个例子

旧架构(像邮寄信件)

scss 复制代码
JS线程:画好图纸 → ✉️ 打包邮寄 →  Shadow线程:量尺寸排版 → ✉️ 再寄 → UI线程:施工盖楼
          (序列化)      等3-5帧              (Yoga)          等3-5帧         (60ms+)

新架构(像视频通话+无人机指挥)

scss 复制代码
JS线程:图纸直接投屏到 C++ 层 → C++:实时算尺寸并指挥 → UI线程:同步施工
          (JSI对象引用)           (共享内存)                   (20ms内)

核心区别:老架构把数据当"包裹"传来传去,新架构让 JS 直接"捏"着原生对象的遥控器,省掉中间商赚差价。

Fabric 的关键改进

  • C++ 层统一布局:Yoga 在 C++ 运行,跨平台一致性更好

  • 异步优先级渲染:支持 React 18 的并发特性

  • 更短的渲染路径:减少线程切换

3. TurboModules

TurboModules 是懒加载的原生模块系统

旧架构 Native Modules有个很大的槽点,就是启动就全加载 :不管用不用,所有原生模块(相机、定位、蓝牙等)在 App 启动时一次性初始化,像开机自启 50 个软件,启动慢。

而新架构有了TurboModules,支持懒加载,大大降低启动时间,降低内存占用

dart 复制代码
// 自动生成的 C++ 接口
struct ToastAndroidSpec : TurboModule {
  void show(String message, int duration);
};

// JS 侧按需加载,首次调用时才初始化
const Toast = TurboModuleRegistry.get('ToastAndroid');
Toast.show('Lazy loaded!'); // 启动时不加载,减少启动时间

四、启动流程详解

1. 应用启动阶段

应用启动阶段是个线性的过程,比较好理解:

关键时间点

  • Before JS:原生代码加载,约 50-200ms
  • JS Load:加载 bundle,影响最大(可拆包优化)
  • JS Exec:执行 JS 代码,初始化模块
  • First Paint:首帧渲染

2. Hermes 引擎的优化

Hermes 是 Facebook 为 RN 定制的 JS 引擎,要比之前启动速度提升30%以上,因为Hermes将字节码做了预编译:

传统:JS 源码 ➔ 解析 ➔ 编译 ➔ 执行

而Hermes:JS ➔ 预编译为 HBC 字节码 ➔ 直接执行

五、所以,为什么RN快?

优化方向 具体措施 原理
启动速度 启用 Hermes、使用 Codegen、减少 bundle 体积 减少 JS 执行和解析时间
通信效率 迁移到 TurboModules、减少 Bridge 调用 同步调用,避免序列化
渲染性能 使用 Fabric、合理使用 Memo/VirtualizedList 减少线程切换和重绘
内存占用 及时销毁监听器、避免在 state 存大数据 防止内存泄漏

六、总结对比

特性 旧架构 (Bridge) 新架构 (JSI+Fabric)
通信方式 异步 JSON 序列化 同步直接调用
线程模型 多线程切换频繁 C++ 层统一处理
启动速度 较慢 提升 30-50%
内存占用 较高 更低
推荐度 ❌ 已废弃 ✅ 必须使用

实际建议 :新项目强制启用新架构(newArchEnabled=true),旧项目逐步迁移。理解运行时原理是写出高性能 RN 代码的基础。

相关推荐
神色自若3 分钟前
vue3 带tabs的后台管理系统,切换tab标签后,共用界面带参数缓存界面状态
前端·vue3
мо仙堡杠把子ご灬3 分钟前
微前端架构实践:避免Vuex模块重复注册的崩溃陷阱
前端
叫我:松哥8 分钟前
基于机器学习的地震风险评估与可视化系统,采用Flask后端与Bootstrap前端,系统集成DBSCAN空间聚类算法与随机森林算法
前端·算法·机器学习·flask·bootstrap·echarts·聚类
呆头鸭L12 分钟前
用vue3+ts+elementPlus+vite搭建electron桌面端应用
前端·vue.js·electron
aPurpleBerry12 分钟前
React Hooks(数据驱动、副作用、状态传递、状态派生)
前端·react.js·前端框架
IT_陈寒13 分钟前
2025年React生态最新趋势:我从Redux迁移到Zustand后性能提升40%的心得
前端·人工智能·后端
前端小臻13 分钟前
react没有双向数据绑定是怎么实现数据实时变更的
前端·javascript·react.js
困惑阿三13 分钟前
CSS 动效交互实验室
前端·css
哟哟耶耶20 分钟前
随笔小计-前端经常接触的http响应头(跨域CORS,性能-缓存-安全,token)
前端·网络协议·http
Allen_LVyingbo23 分钟前
病历生成与质控编码的工程化范式研究:从模型驱动到系统治理的范式转变
前端·javascript·算法·前端框架·知识图谱·健康医疗·easyui