Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

React Native 跨平台思考 #96

Open
pfan123 opened this issue May 9, 2021 · 1 comment
Open

React Native 跨平台思考 #96

pfan123 opened this issue May 9, 2021 · 1 comment

Comments

@pfan123
Copy link
Owner

pfan123 commented May 9, 2021

跨平台其实是一个老生常谈的话题,技术方案也是历经变迁,但始终热点不断,究其原因有二:

  • 首先,移动端原生技术需要配备 iOS 和 Android 两套团队和技术栈,且存在发版周期限制,开发效率上存在天然缺陷;
  • 其次,原生跨平台技术虽然「出道」较早,但是各方案都难以做到完美,因此也没有大一统的技术垄断

——因此,周期性地就有问题:「XXX 跨端项目现在是否凉了」、「XXX 跨端项目以后发展前景如何」?

其实这些问题都有一个统一的回答:看(业务、团队等)场景,看(业务、团队等)需求。每一种原生跨端方案在一定历史阶段内,都有其存在的意义和价值。就此,我不再聚焦「React Native (营销或前景上)是否凉了」,仅从技术层面进行简单分析 React Native 究竟完了没完。

我们先来简单 recap 一下跨端技术发展之路,总结如下图:

img

早期出现了 Cordova、Ionic 等框架,它们本质上都是使用 HTML、CSS 和 JavaScript 进行跨平台原生应用的开发。该方案说到底是在 iOS 和 Androd 上运行 Web 应用,因此也存在较多问题,比如:

  • JavaScript Context 和原生通信频繁,导致性能体验较差;
  • 页面逻辑由前端负责,组件也是前端渲染,也造成了性能短板;
  • 运行 JavaScript 的 WebView 内核在各平台上不统一;
  • 国内厂商对于系统的深度定制,导致内核碎片化。

这里不再赘述,我们主要聚焦一下 React Native。因为 hybrid 时代方案缺陷,新一代的 Hybrid 跨平台方式,以 React Native 为代表的方案就诞生了,这种方案主要思想是:开发者依然**使用 Web 语言(如 React 框架或其他 DSL),但渲染基本交给原生平台处理。**这样一来,在视图层面就可以摆脱 WebView 的束缚,保障了开发体验和效率,以及使用性能。我把这种技术叫作基于 OEM 的 Hybrid 方案。

React Native 脱胎于 React 理念,它将数据与视图相隔离,React Native 代码中的标签映射为虚拟节点,由原生平台解析虚拟节点并渲染出原生组件。美好的愿景是:开发者使用 React 语法,同时开发原生应用和 Web 应用,其中组件渲染、动画效果、网络请求等都由原生平台来负责。

RN现在主要有3个线程

  1. JS thread。JS代码执行线程,负责逻辑层面的处理。Metro(打包工具)将React源码打包成一个单一JS文件(就是图中JSBundle)。然后传给JS引擎执行,现在ios和android统一用的是JSC。
  2. UI Thread(Main Thread/Native thread)。这个线程主要负责原生渲染(Native UI)和调用原生能力(Native Modules)比如蓝牙等。
  3. Shadow Thread。 这个线程主要是创建Shadow Tree来模拟React结构树。Shadow Tree可以类似虚拟dom。RN使用Flexbox布局,但是原生是不支持,所以Yoga就是用来将Flexbox布局转换为原生平台的布局方式。

React Native Architecture Components | LITSLINK Blog整体技术架构如下图:

img

React Native 主要由:

  • JavaScript
  • C++ 适配层
  • iOS/Androd

三层组成,最重要的 C++ 层实现了动态链接库,起到了衔接适配前端和原生平台作用,这个衔接具体指:使用 JavaScriptCore 解析 JavaScript 代码(iOS 上不允许用自己的 JS Engine,iOS 7+ 默认使用 JavaScriptCore,Android 也默认使用 JavaScriptCore),通过 MessageQueue.js 实现双向通信,实际上通信格式类似 JSON-RPC。

这样的效果显而易见,通过前端能力,实现了原生应用的跨平台,快速编译、快速发布

但是为什么有人觉得 React Native 要完了呢?


对 React Native 来说,上述数据通信过程是异步的,通信成本很高。除此之外,目前 React Native 仍有部分组件和 API 并没有实现平台统一,也在一定程度上需要开发者了解原生开发细节。正因如此,社区上也出现了著名文章《React Native at Airbnb》,文中表示 Airbnb 团队在技术选型上将会放弃 React Native。

这...React Native 要凉?


在我看来,像 airbnb 那样放弃 React Native,拥抱新的跨平台技术并不是每个团队都有实力和魄力施行的,而改造 React Native 是另外一些团队做出的选择。

比如携程的 CRN(Ctrip React Native)以及美团的 MRN。他们在 React Native 基础上,抹平了 iOS 和 Android 端组件开发差异,做了大量性能提升的工作。更重要的是,依托于 CRN,它在后续的产品 CRN-Web 也做了 Web 支持和接入。

另外更重要的是,**React Native 也在成长。**上文我们提到,React Native 通过数据通信架起了 Web 和原生平台的桥梁,而这个数据通信方式是异步的。React 工程经理 Sophie Alpert 将这种这样的设计获得了线程隔离的便利,具备了尽可能的灵活性,但是这也意味着 JavaScript 逻辑与原生能力永远无法处在同一个时空,无法共享一个内存空间。

基于这个问题,新的 React Native 技术架构将从三个方面进行革新。

  1. 改变线程模型(Threading Model),以往 React Native 的 UI 更新需要在三个不同的线程进行,新的方案使具有高优先级更新的线程,直接同步调用 JavaScript;同时低优先级的 UI 更新任务不会占用主线程。
  2. 引入异步渲染能力,实现不同优先级的渲染,同时简化渲染数据信息。
  3. 简化 Bridge 实现,使之更轻量可靠,使 JavaScript 和原生平台的调用更加高效。

新架构如下图:

React Native New Architecture | LITSLINK Blog

举个例子,新的架构这些改造能够使得“手势处理”——这个 React Native 老大难问题得到更好的解决,比如新的线程模型能够使手势触发的交互和 UI 渲染效率更高,减少异步通信更新 UI 成本,使视图尽快响应用户的交互。

上述重构的核心之一其实是使用基于 JavaScript Interface (JSI) 的新 Bridge 方案来取代之前的 Bridge 方案。新的 Bridge 方案由两部分组成:

  • Fabric,新的 UIManager
  • TurboModules,新的原生模块

其中 Fabric 运行 UIManager 直接用 C++ 生成 Shadow Tree,而不需要走一遍老架构的 React → Native → Shadow Tree → Native UI 路径。这就减少了通信成本,提升交互性能。这个过程依赖于 JSI,JSI 并不和 JavaScriptCore 绑定,因此我们可以实现引擎互换(比如使用 V8,或任何其他版本的 JavaScriptCore)。同时,JSI 可以获取 C++ Host Objects,并调用 Host Objects 上的方法,这样能够完成 JavaScript 和原生平台的直接感知,达到“所有线程之间的互相调用操作”,因此我们就不再依赖“将传递消息序列化,并进行异步通信”了。这也就消除了异步通信带来的拥塞等问题。新的方案也允许 JavaScript 代码仅在真正需要时加载每个模块,如果应用中并不需要使用 Native Modules(例如蓝牙功能),那么它就不会在程序打开时被加载,这样就可以提升应用的启动时间。


我们再来看看最炙手可热的 Flutter,Flutter 采用了 Dart 编程语言,它在技术设计上不同于 React Native 的一个显著特点是:Flutter 并非使用原生平台组件进行渲染。比如在 React Native 中,一个 <view> 组件会被最终编译为 iOS 平台的 UIView Element 以及 Android 平台的 View Element,但在 Flutter 中,Flutter 自身提供一组组件集合,这些组件集合被 Flutter 框架和引擎直接接管。如下图(出自 Cross-Platform Mobile Development Using Flutter):

Figure 3: How Flutter works

Flutter 组件依靠自身高性能的渲染引擎进行视图的渲染。具体来说,每一个组件会被渲染在 Skia 上,Skia 是一个 2D 的绘图引擎库,具有跨平台特点。Skia 唯一需要的就是原生平台提供 Canvas 接口,实现绘制

目前来看,Flutter 具备其他(主流)跨平台方案所不具备的技术优势,它是更底层,真正意义上的跨端,未来前景大好。但作为后入场者,也存在生态小、学习成本高等障碍,也正因为更底层,存在需要通过 bridge,调用原生平台能力的成本困扰。

总结

说了这么多,讲了一堆「技术原理」,那么到底 React Native 凉没凉呢?

其实这不是任何一个「专家」或背书的团队应该回答你的问题,需求不同、立场不同,它们都不会完全靠谱。当你了解个各个方案的技术原理,掌握了 React Native 发展路线(包括新版本的技术重构)等重要信息,结合自己业务和团队的需求、痛点,进行选择?

Other Resources

Cross-Platform Mobile Development Using Flutter

new-react-native-architecture

深入剖析 React Native 下一代架构重构

React Native 新架构分析

React Native 原理与新架构入门

React Native发布新一代JS引擎Hermes

深入理解JSCore

@zerosrat
Copy link

存在需要通过 bridge,调用原生平台能力的成本困扰 -> 不存在

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants