共计 5039 个字符,预计需要花费 13 分钟才能阅读完成。
一、前言
7 月 14 日,React Native 外围团队的 Joshua Gross 在 Twitter 说,RN 的新架构曾经在 Facebook 外部落地了,并且 99% 的代码曾经开源。其实,早在 2018 年 6 月,Facebook 官网就发表了大规模重构 React Native 的打算及重构路线图,目标是为了让 React Native 更加轻量化、更适应混合开发,靠近甚至达到原生的体验。
这次的架构降级对于 React Native 意义重大,依照官网的说法,降级后的 RN 性能将失去大幅晋升,次要是解决诟病已久的性能问题,下图是 React Native 的一个版本公布阐明。
为了让 RN 更轻量化、更适应混合开发,靠近甚至达到原生的体验,React Native 的优化措施包含以下几个方面:
- 扭转线程模型,UI 更新不再同时须要在三个不同的线程上触发执行,而是能够在任意线程上同步调用 JavaScript 进行优先更新,同时将低优先级工作推出主线程,以便放弃对 UI 的响应。
- 引入异步渲染能力,容许多个渲染并简化异步数据处理。
- 简化了 JSBridge 的渲染逻辑,优化了底层渲染架构,让它更快更轻量。
二、新老架构比照
对于有过 RN 开发教训的都晓得,原有 RN 架构 JS 层与 Native 的通信过多的依赖 Bridge,而且是异步通信,这就造成一些通信频率较高的交互和设计就很难实现,同时也影响页面的渲染。而新架构也正是从这一点登程,对 Bridge 这层做了大量的革新,使得 UI 和 API 的调用,从原有异步形式调整到能够同步或者异步与 Native 通信,解决了频繁通信的瓶颈问题。
同时,新架构应用 JSI(全称是 JavaScript Interface)代替原来的 Bridge,JS 层间接调用 C++ 层进而调用 Java/OC 的形式,实现 JS 和 Java/OC 之间的互相操作,大大提高了通信的效率。得益于 JSI 的全新架构,JavaScript 能够间接调用 Native 模块的办法。
三、旧架构
在介绍新架构之前,咱们先看一下 RN 框架目前的架构,以及它的一个毛病,以及为什么 Facebook 要重构整个框架。目前,RN 应用的架构次要蕴含 Native、JavaScript 与 Bridge 三个局部。Native 治理 UI 更新及交互,JavaScript 调用 Native 能力实现业务性能,Bridge 在二者之间传递音讯,整个架构如下图。
能够看到,React Native 的架构还是很清晰的。最上层提供类 React 反对,运行在 JavaScriptCore 提供的 JavaScript 运行时环境中,Bridge 层将 JavaScript 与 Native 世界连接起来。而后,Bridge 分为了三局部,其中 Shadow Tree 用来定义 UI 成果及交互性能,Native Modules 提供 Native 性能(比方相册、蓝牙),而他们之间的互相通信 应用的是 JSON 异步音讯。
3.1 Bridge
在当初的架构中,Bridge 层是 React Native 技术的要害,它具备以下一些特点:
- 异步(asynchronous):不依赖于同步通信。
- 可序列化(serializable):保障所有 UI 操作都能序列化成 JSON 并转换回来。
- 批处理(batched):对 Native 调用进行排队,批量解决。
3.2 线程模型
在旧架构中,React Native 一共有 3 个线程,别离是 UI Thread、Shadow Thread 和 JS Thread。
- UI Thread:Android/iOS(或其它平台)利用中的主线程。
- Shadow Thread:进行布局计算和结构 UI 界面的线程。
- JS Thread:React 等 JavaScript 代码都在这个线程执行。
它们之前的关系如下图:
3.3 启动流程
对于 RN 利用来说,App 启动后首先须要初始化 React Native 运行时环境(即 Bridge),Bridge 筹备好之后接着能力运行 JS 代码,而后执行 Native 渲染。残缺的启动过程如下:
其中,初始化 Bridge 波及到如下过程:
能够看到,初始化 Bridge 次要分为 4 个步骤:
- 加载 JavaScript 代码:开发模式下从网络下载,生产环境从设施存储中读取
- 初始化 Native Modules:依据 Native Module 注册信息,加载并实例化所有 Native Module
- 注入 Native Module 信息:取 Native Module 注册信息,作为全局变量注入到 JS Context 中
- 初始化 JavaScript 引擎:即 JavaScriptCore
3.4 渲染流程
后面说过,React Native 一共有 3 个线程,别离是 UI Thread、Shadow Thread 和 JS Thread。
在渲染流程中,JS 线程将视图信息(构造、款式、属性等)传递给 Shadow 线程,创立出用于布局计算的 Shadow Tree,Shadow 线程计算好布局之后,再将残缺的视图信息(包含宽高、地位等)传递给主线程,主线程据此创立视图。
对于须要响应的事件来说,则先由主线程将相干信息打包成事件消息传递到 Shadow 线程,再依据 Shadow Tree 建设的映射关系生成相应元素的指定事件,最初将事件传递到 JS 线程,执行对应的 JS 回调函数。
残缺的渲染流程如下图:
通过下面的剖析,不难发现当初的架构是强依赖 nativemodule,也就是大家通常说的 bridge,对于简略的 Native API 调用来说性能还能承受,而对于 UI 来说,每次的操作都是须要通过 bridge 的,包含高度计算、更新等,且 bridge 限度了调用频率、只容许异步操作,导致一些前端的更新很难及时反馈到 UI 上,特地是相似于滑动、动画,更新频率较高的操作,所以常常能看到白屏或者卡顿。
四、新架构
当初的架构,JS 层与 Native 的通信都太依赖 Bridge,导致一些通信频率较高的交互和设计就很难实现,同时也影响了渲染性能。基于下面的问题,在新的设计上,React Native 提出了几个新的概念和设计:JSI、Fabric 和 TuborModule。
- JSI(JavaScript interface):本次架构重构的外围重点,也正是 JSI 的缘故,原有重度依赖的 Native Bridge 架构失去解耦,JS 层与 Native 的通信老本大大降低。
- Fabric:依赖 JSI 的设计,将旧架构下的 shadow tree 层移到 C++ 层,这样能够通过 JSI 实现前端组件对 UI 组件的一对一管制,解脱了旧架构下对于 UI 的异步、批量操作,升高了通信老本。
- TuborModule:新的原生 API 架构,替换了原有的 Java module 架构,数据结构上除了反对根底类型外,开始反对 JSI 对象,让前端和客户端的 API 造成一对一的调用。
上面是 React Native 的新的架构示意图:
能够看到,在新的架构计划上,Bridge 层被新的 JSI 代替,不同于之前间接将 JavaScript 代码输出给 JSC,新的架构中引入了一层 JSI(JavaScript Interface)。作为 JSC 之上的形象,JSI 用来屏蔽 JavaScript 引擎的差别,容许换用不同的 JavaScript 引擎(比方 Hermes)。
新的 JSI 层又蕴含了 Fabric 和 TurboModules 两局部。其中,Fabric 负责管理 UI,TurboModules 负责与 Native 交互。Fabric 以更现代化的形式去实现 React Native 的渲染层,简化之前渲染流程中简单跨线程交互流程(React -> Native -> Shadow Tree -> Native UI)。而 TurboModules 也反对按需加载 Native 模块,从而缩短了 RN 初始化 Native 模块带来的性能开销。
4.1 JSI
后面说过,为了降级 RN 的架构,RN 提出了全新的 JSI 的概念,有了 JSI 之后,JavaScript 能够间接持有 C++ 对象的援用,并调用其办法。JSI 在 0.60 后的版本就曾经开始反对,它是 Facebook 在 JS 引擎上设计的一个适配架构,它容许开发者向 JavaScript 运行时注册办法的 JavaScript 接口,而这些注册办法齐全能够用 C++ 进行编写。除此之外,JSI 还带来了如下的一些个性:
- 标准化的 JS 引擎接口,React Native 能够替换 v8、Hermes 等引擎。
- 优化降级 JS 和原生 java 或者 Objc 的通信,然而不同于 JSBridge 采纳的是内存共享、代理类的形式,为了实现和 Native 端间接通信,JSI 提供了一层 C++ 层实现的 JSI::HostObject,该数据结构反对 propName, 同时反对从 JS 传参。
- 原有 JS 与 Native 的数据通讯采纳 JSON 和根底类型数据,但有了 JSI 后,数据类型更丰盛,反对 JSI Object。
所以,在新架构下 API 调用流程:JS->JSI->C++->JNI->JAVA,每个 API 更加独立化,不再须要全副依赖 Native module。但这也带来了另外一个问题,就是开发者在设计一个 API 须要封装 JS、C++、JNI、Java 等一套接口,对开发者的要求还是比拟高的。不过,好在 Facebook 提供了一个 codegen 模块,能够帮忙开发者实现根底代码和环境的搭建。
对于封装 jsi 的过程,能够参考开发 JSI Module
4.2 Fabric
Fabric 是 RN 新架构的 UI 框架,和原有的 UImanager 框架的作用相似,不过 UImanager 的渲染性能与原生端组件和动画的渲染性能还是有很大的差距的。举个比拟常见的问题,Flatlist 疾速滑动的状态下,会存在很长的白屏工夫,交互比拟强的动画、手势很难反对,因而 RN 采纳了全新的 Fabric 框架。
简略来说,JS 层新设计了 FabricUIManager,目标是反对 Fabric render 实现组件的渲染与更新。因为采纳了 JSI 的设计,FabricUIManager 能够和 cpp 层间接进行通信,对应 C++ 层 UIManagerBinding,其实每个操作和 API 调用都有对应创立了不同的 JSI,从这里就彻底解除了原有的全副依赖 UIManager 单个 Native bridge 的问题,同时组件大小的 measure 也解脱了对 Java、bridge 的依赖,间接在 C++ 层 shadow 实现,晋升渲染效率。
有了 JSI 后,以前批量依赖 bridge 的 UI 操作,都能够同步的执行到 c++ 层,而在 c++ 层,新架构实现了一个 shadow 层的搭建,而旧架构是在 java 层实现,所以从这方面来说,渲染的性能也失去了大幅的晋升。
4.3 TurboModule
在之前的架构中,Native Modules(无论是否须要用到)都要在利用启动时进行初始化,因为 Native 不晓得 JavaScript 将会调用哪些功能模块。而新的 TurboModules 容许按需加载 Native 模块,并在模块初始化之后间接持有其援用,不再依附音讯通信来调用模块性能。因而,利用的启动工夫也会有所晋升。并且,0.64 版本曾经反对 TurboModule 的应用。
总的来说,TurboModule 的设计就是为了不便 JS 能够间接调用到 c++ 的办法。
4.4 CodeGen
新架构 UI 减少了 C++ 层的 shadow、component 层,而且大部分组件都是基于 JSI,因此开发 UI 组件和 API 的流程更简单了,要求开发者具备 c++、JNI 的编程能力,为了不便开发者疾速开发 Facebook 也提供了 codegen 工具,帮忙生成一些自动化的代码。
CodeGen 工具参考,同时,因 codegen 目前还没有正式 release,对于如何应用的文档简直没有,还得等开源后才会晓得。
另外,JSI、Turbormodule 曾经在最新的版本上曾经能够体验,而且开发者社区也用 JSI 开发了大量的 API 组件,例如:
- https://github.com/mrousavy/r…
- https://github.com/mrousavy/r…
- https://github.com/mrousavy/r…
- https://github.com/software-m…
- https://github.com/BabylonJS/…
- https://github.com/craftzdog/…
- https://github.com/craftzdog/…
从最新的更新状况来看,RN 的新架构离公布仿佛曾经进入倒计时,作为 RN 的忠诚粉丝,也始终心愿 RN 可能尽快的公布 1.0 版本。