React Native 背景介绍
React Native 框架技术源于 Facebook 技术团队,基于其前端框架 React 发展而来,专为开发移动端应用程序而设计。
App 开发的四种模式
移动应用开发主要有四种模式:原生开发、网页应用、混合模式应用以及 React Native。
原生 App 开发使用 Android(Java)和 iOS(Objective-C)的原生框架。这种模式性能与用户体验最佳,动画流畅,交互性好,但需要两套开发与维护团队,成本高,维护复杂,且客户端代码需打包发布,无法动态更新。
网页应用开发 App 即 Web App,通常被称为 H5 应用,在移动浏览器中运行。其开发人员需求少,页面可动态修改,但性能与体验受浏览器限制,流量消耗较大。
Hybrid App 混合模式移动应用介于原生与 Web 之间,拥有原生 App 外壳,内容从云端拉取。开发成本相对较小,页面可随时更新,但性能和体验不及原生,且前期需要原生技术人员搭建外壳。
React Native App 是 Facebook 为改进 Hybrid App 不足而设计的方案。它使用 JSX 编写原生界面,JavaScript 通过 JSBridge 调用原生 API 进行渲染和通信。其设计思路与 Node.js 异曲同工,均通过一个转译桥梁实现不同语言间的通信,使 JavaScript 能够调用移动端原生 API。React Native 开发成本小,只需学习 JavaScript 和 React,效率与体验接近原生,但学习有一定成本,文档相对较少,且不能动态修改页面。与之类似的框架还有阿里的 Weex 和 Google 的 Flutter,但二者社区成熟度相对较低。
JavaScript Core
JavaScript Core 是 React Native 的核心驱动力之一,所有 JavaScript 代码都通过 JavaScript 引擎编译执行,包括 React 的 JSX。除了 JavaScript Core,常见的引擎还有 Google 的 V8 引擎和 Mozilla 的 SpiderMonkey 引擎。
浏览器工作原理与 React Native 对比
在浏览器中,UI 控件调用操作系统绘制指令来绘制图形。React Native 同样使用类 XML 语言描述结构,用 StyleSheet 规划样式,但其 UI 控件调用的是自身的 Android 和 iOS 控件。JavaScript 在 React Native 中的作用是向原生组件发送指令以完成 UI 渲染,因此 JavaScript Core 至关重要。
React Native、React 与 JavaScript Core 的关系
React 是一个纯 JavaScript 框架,依赖 JavaScript 引擎解释执行,并通过 Virtual DOM 实现数据驱动编程和 JSX 语法。但它无法触及操作系统底层,如驱动、线程等。
React Native 的情况更复杂:原生代码驱动 JavaScript 引擎,引擎解析执行 React 及相关代码,然后将计算结果返回给原生代码,最终驱动设备硬件。它利用 Virtual DOM 和数据驱动简化开发,由原生 UI 控件负责绘制,保证了原生体验。由于 JavaScript 无需编译,其运行效率高于基于 HTML5 的 PhoneGap 等技术,后者涉及大量耗时的 DOM 操作。
因此,可以总结为:React Native = JavaScript Core + React.js + Bridges。
React Native 架构分析
React Native 的架构可分为三层:
- Java层:负责 Native 的 UI 渲染和底层功能调用,核心是 react-native.jar,封装了 Module、Registry、Bridge 等接口。
- C++层:主要封装了 JavaScriptCore,用于解析 JavaScript 代码。
- JS层:使用 JavaScript 进行事件分发和 UI 编写,包括 Component(用 JSX 构建虚拟 DOM)、Lifecycle(生命周期管理)和 Layout(使用 FlexBox 布局,通过 css-layout 转换为 Native 端代码,目前不支持 CSS3)。
Bridge 通信机制
Bridge 是 JavaScript 与 Native 通信的桥梁,它将本地存储、图片资源访问、图形绘制、网络访问等原生功能封装成 JavaScript 接口并注入 JavaScript 引擎供调用。每个支持 React Native 的原生功能都必须有一个原生模块和一个对应的 JavaScript 模块。
Bridge 原生代码管理原生模块并使其可被 React 调用。JavaScript 与 Native 之间不传递指针,所有参数均通过字符串传递。
Bridge 各模块介绍
- RCTRootView:React Native 的加载入口,初始化后持有 RCTBridge,加载 JavaScript Bundle 并初始化运行环境,然后启动 UI 绘制。
- RCTBridge:负责加载和初始化,注册实现了 RCTBridgeModule protocol 的类,并创建持有 RCTBatchedBridge。
- RCTBatchedBridge:处理 Native 与 JavaScript 之间的相互调用(信息通信)。
- RCTJavaScriptLoader:实现远程代码加载、热更新和开发环境代码加载。
- RCTContextExecutor:封装 JavaScript 与 Native 代码的互相调用逻辑。
- RCTModuleData:加载并管理与 JavaScript 交互的所有原生代码,将其封装成 JavaScript 模块。
- RCTModuleMethod:记录所有原生导出函数的地址,并生成字符串映射,翻译所有 JavaScript 到 Native 的调用。
- MessageQueue:负责跳出 JavaScript 引擎,记录原生接口地址和对应的 JavaScript 函数名,在 JavaScript 调用时将其转发给原生接口。
Bridge 如何工作
JavaScript 与 iOS 通信使用 JavaScript Core,与 Android 通信使用 Hermes。
React Native 的线程
React Native 主要包含三个线程:
- JS Thread:执行线程,负责逻辑处理。Metro 将 React 源码打包成 bundle 文件,由 JavaScript 引擎执行。
- UI Thread:主线程,负责原生 UI 渲染和调用原生能力。
- Shadow Thread:创建 Shadow Tree 来模拟 React 结构树,并通过 Yoga 引擎将 Flexbox 布局转换为原生布局。
关键概念:
- UIManager:在 Native 端唯一有权限调用客户端 UI 的模块。
- Shadow Node:Native 的组件树,用于监听 UI 变化,类似于虚拟 DOM。
- Yoga:Facebook 开源的布局引擎,用于转换 Flexbox 布局。
打开 App 时发生什么
以下代码可以用于观察 Bridge 运行过程:
import MessageQueue from 'react-native/Libraries/BatchedBridge/MessageQueue.js'
MessageQueue.spy(true, msg => console.log(msg))
在 App 中加入一个 View 组件:
<View style={{
backgroundColor: 'green',
width: 100,
height: 100
}}/>
流程如下:
- 用户点击 App 图标。
- UIManager 线程加载所有 Native 库和组件。
- Native 侧准备就绪后通知 JS 线程,JS 侧开始加载 bundle 文件。
- JavaScript 通过 Bridge 发送 JSON 数据到 Native,描述如何创建 UI。所有 Bridge 通信都是异步的,以避免阻塞 UI。
- Shadow 线程最先收到消息,创建 UI 树。
- Yoga 引擎计算布局并转换为 Native 布局。
- UI Manager 执行操作,展示 Native UI。
Bridge 的缺点与优化原则
Bridge 存在以下缺点:
- JavaScript 与 Native 处于不同领域,无法相互感知或共享内存。
- 通信基于异步 Bridge,不能保证数据及时传达。
- JSON 传输大数据速度慢,且所有传输都是数据复制。
- 无法同步更新 UI,可能导致列表滚动时卡顿或闪烁。
- React Native 代码库庞大,修复 Bug 和社区贡献效率相对较慢。
优化 Bridge 缺点的三个原则:
- JavaScript 与 Native 避免通信,或直接绕过 Bridge。
- 减少通信次数,将多个请求合并。
- 减小 JSON 数据的大小。
React Native 新架构
Facebook 团队正在重构 React Native 框架,新架构主要包括:
- JSI:将替换 Bridge,是一个用 C++ 编写的 JavaScript 引擎接口。它允许 JavaScript 直接获取 C++ 对象引用并调用方法,实现同步通信,并抹平 JavaScript Core 与 Hermes 的差异。
- Fabric:UI Manager 的新名称,负责 Native UI 渲染。它通过 JSI 导出 Native 函数,使 JavaScript 能直接引用和调用,支持同步操作和渲染优先级(如 React 的 Concurrent 和 Suspense 模式)。
- CodeGen:将带有静态类型检查的 JavaScript 代码自动转换为 Fabric 和 TurboModules 使用的原生代码,提升性能并减少数据传输出错。
- TurboModules:Native 组件的新名称,实现懒加载(而非启动时全部加载),并通过 JSI 导出,使 JavaScript 可以直接引用和同步调用。
- Lean Core:旨在减轻 React Native 核心库的负担,让社区更好地解决问题。
新架构下的 App 启动流程
- 点击 App 图标。
- Fabric 加载 Native 侧。
- Native 侧就绪后通知 JS 线程,JS 侧加载所有 bundle 文件。
- JavaScript 通过 Native 函数引用调用 Fabric,Shadow Node 创建 UI 树。
- Yoga 进行布局计算并转换。
- Fabric 执行操作并显示 UI。
新架构通过移除 Bridge 提升了性能,支持同步操作,加快了启动速度,并减小了 App 体积。