写在前面
上一篇(React Native 架构一览)从设计、线程模型等方面介绍了 React Native 的现有架构,本篇将分析这种架构的局限性,以及 React Native 正在进行的架构升级计划
一.现有架构的局限性
最初的设计也带来了一些限制:
异步:无法将 JavaScript 逻辑直接与许多需要同步答案的 Native API 集成
批处理:很难让 React Native 应用调用 Native 实现的函数
可序列化:存在不必要的 copy,而不是直接共享内存
这些问题在 Native + React Native 的混合应用中尤其突出:
For apps that are entirely built in React Native, these restrictions are usually bearable. But for apps with complex integration between React Native and existing app code, they are frustrating.
二.架构升级计划
因此,2018 年 6 月提出大规模重构的计划,目的是更好地支持混合应用:
We’re working on a large-scale rearchitecture of React Native to make the framework more flexible and integrate better with native infrastructure in hybrid JavaScript/native apps.
具体的,有 3 点重大改动:
线程模型:允许在任意线程中同步调用 JavaScript执行高优先级的更新,UI 更新不再非要跨 3 个线程才能进行
React:支持 React 16+的新特性,包括async rendering、Data Fetching 等等
Bridge:精简优化,允许 Native 与 JavaScript 之间的直接调用
支持同步调用让之前很难实现的一些东西成为了可能,例如跨语言的调用栈追踪
对应到架构图中,相当于对每一层进行单独优化:
React 层:增强 JavaScript 类型安全,并支持 React 16+新特性
JavaScript 层:引入 JSI,允许替换不同的 JavaScript 引擎
Bridge 层:划分成 Fabric 和 TurboModules 两部分,分别负责 UI 渲染与 Native 模块
Native 层:精简核心模块,将非核心部分拆分出去作为社区模块独立更新维护
初步估计,这些重构工作预期在 2019 年底或 2020 年初完成:
It’s likely this massive piece of work will reach its conclusion around Q4 2019 or Q1 2020, but there are no confirmed dates.
P.S.目前(2019/9/8)除已完成的 JSI 外,其余重构计划仍在进行中,具体见The New React Native Architecture Explained: Part Four
三.增强 JavaScript 类型安全
主要变化在于,提供 CodeGen 工具来保证消息通信的类型安全,以解决 JavaScript 与 Native 通信中被广为诟病的 Bridge API 数据类型问题:
We also experienced many issues in which the types coming from JavaScript were unexpected. For example, integers were often wrapped by strings, an issue that isn’t realized until it is passed over a bridge. To make matters worse, sometimes iOS will fail silently while Android will crash.
(摘自React Native at Airbnb: The Technology)
另一方面,类型约束对通信性能也有一定帮助:
This automation will speed up the communication too, as it’s not necessary to validate the data every time.
四.引入 JSI
上层 JavaScript 代码需要一个运行时环境,在 React Native 中这个环境是 JSC(JavaScriptCore)。不同于之前直接将 JavaScript 代码输入给 JSC,新的架构中引入了一层 JSI(JavaScript Interface),作为 JSC 之上的抽象,用来屏蔽 JavaScript 引擎的差异,允许换用不同的 JavaScript 引擎(如最近推出的Hermes)
更重要的,有了 JSI 之后,JavaScript 还能持有 C++对象的引用,并调用其方法:
By using JSI, JavaScript can hold reference to C++ Host Objects and invoke methods on them.
从而允许 JavaScript 与 Native 的直接调用,而不必通过跨线程消息通信,省去序列化/反序列化的成本,还能减轻 Bridge 的通信压力(如大量消息排队堵车)
同时JSI 所在的 C++层也可以作为复用 Native 代码的一种方式,拥有 Native 的天然支持:
Android:通过 JNI(Java Native Interface)调用 C 或 C++模块
iOS:Objective-C 默认支持
五.重构 Bridge 层
新的 Bridge 层被划分成 Fabric 和 TurboModules 两部分:
Fabric:负责管理 UI
TurboModules:负责与 Native 交互
Fabric 期望以更现代化的方式去实现 React Native 的渲染层,简化之前渲染流程中复杂跨线程交互(React -> Native -> Shadow Tree -> Native UI)。具体的,直接在 C++层创建 JavaScript 与 Native 共享的 Shadow Tree,并通过 JSI 层将 UI 操作接口暴露给 JavaScript,允许 JavaScript 直接控制高优先级的 UI 操作,甚至允许同步调用(应对列表快速滚动、页面切换、手势处理等场景)
之前所有 Native Modules(无论是否需要用到)都要在应用启动时进行初始化,因为 Native 不知道 JavaScript 将会调用哪些功能模块。而新的TurboModules 允许按需加载 Native 模块,并在模块初始化之后直接持有其引用,不再依靠消息通信来调用模块功能。因此,应用的启动时间也会有所提升
六.精简核心模块
理论上,React Native 应该是通用的,对平台无感知,这是能够支持Web、Windows等不同平台的关键
虽然 Native 不在 React Native 的掌控中,无法垂直地深入优化,但可以进行横向的精简,将非核心的部分代码拆分出去作为社区模块,如 AsyncStorage、ImageStore、MaskedViewIOS、NetInfo 等等。一方面缩减包体积,另一方面也有利于这些模块的独立更新维护