React Native 在 Airbnb(译文)

在Android,iOS,Web和跨平台框架的横向对比中,React Native自己是一个相对较新且快速开发移动的平台。两年后,咱们能够确定地说React Native在不少方面都是革命性的。这是移动设备的范例转变,咱们可以从中受益不少。然而也有明显的痛点,它的优势不只仅是这些javascript

优势

跨平台 React Native 的主要好处是,您编写的代码能够在Android和iOS上本机运行。使用React Native的大多数功能都可以实现95-100%的共享代码,0.2%的文件是特定于平台的(android.js / ios.js)。css

统一设计语言系统(DLS)
咱们开发了一种名为DLS的跨平台设计语言。咱们有Android,iOS,React Native和每一个组件的Web版本。拥有统一的设计语言能够编写跨平台功能,由于它意味着设计,组件名称和屏幕在不一样平台上是一致的。可是,咱们仍然能够在适用的状况下作出适合平台的决策。例如,咱们使用Android 上的原生工具栏和iOS 上的UINavigationBar,咱们选择隐藏Android上的披露指标,由于它们不符合Android平台设计指南。html

咱们选择重写组件而不是包装本机组件,由于为每一个平台单独制做适合平台的API更加可靠,并减小了可能不知道如何正确测试React Native中的更改的Android和iOS工程师的维护开销。可是,它确实会致使同一组件的本机版本和React Native版本不一样步的平台之间出现碎片。java

react
React是最受欢迎的 Web框架。它简单而强大,能够很好地扩展到大型代码库。咱们特别喜欢它的特色:node

  • 组件: React组件经过明肯定义的道具和状态强制分离关注点。这是React可扩展性的主要贡献者。
  • 简化的生命周期: Android以及在较小程度上,iOS生命周期很是复杂。功能性反应性React组件从根本上解决了这个问题,使学习React Native比学习Android或iOS简单得多。
  • 声明:react有助于保持咱们的UI同步与底层状态的声明性质。

迭代速度
在React Native中进行开发时,咱们可以在一两秒内可靠地使用热从新加载来测试Android和iOS上的更改。尽管构建性能是咱们原生应用程序的首要任务,但它从未接近咱们使用React Native实现的迭代速度。充其量,本机编译时间为15秒,但完整版本可能高达20分钟。react

基础环境的搭建成本
咱们开发与本机基础架构是普遍集成。全部核心部分(如网络,国际化,测试,共享组件转换,设备信息,账户信息等)都包含在一个React Native API中。这些桥接是一些比较复杂的桥接,由于咱们但愿将现有的Android和iOS API包装成React的一致和规范。虽然经过快速迭代和新基础设施的开发使这些桥接保持最新状态是一个不断追赶的游戏,但基础设施团队的投资使产品开发工做变得更加容易。android

若是不对基础环境进行大量投入,ReactNative将致使下降开发人员和用户体验。所以,咱们不相信React Native能够简单地添加到现有应用程序而无需大量持续投入。ios

性能
React Native最大的担心之一就是它的性能。可是,在实践中,出现的次数相对来讲仍是比较少的。咱们的大多数React Native屏幕都像咱们的原生屏幕同样流畅。性能一般被认为是单一维度。咱们常常看到移动工程师看JS并认为“比Java慢”。可是,在许多状况下,从主线程移动业务逻辑和布局实际上改善了渲染性能。css3

当咱们确实看到性能问题,他们一般由过多的渲染引发的有效利用是缓解shouldComponentUpdate,removeClippedSubviews,并更好地利用终极版。git

可是,初始化和首次渲染时间(以下所述)使得React Native在启动屏幕,深层连接方面表现不佳,而且在屏幕之间导航时增长了TTI时间。此外,丢帧的屏幕很难调试,由于Yoga在React Native组件和本机视图之间进行转换。

Redux
咱们使用Redux进行状态管理,咱们发现它有效并阻止UI与状态不一样步,并在屏幕上轻松实现数据共享。然而,学习曲线相对困难。咱们为一些常见模板提供了生成器,但在使用React Native时,它仍然是最具挑战性的部分之一和混淆源。值得注意的是,这些挑战并不是特定于React Native。

由Native支持
由于React Native中的全部内容均可以经过本机代码进行桥接,因此咱们最终可以构建许多咱们在开始时不肯定的内容,例如:

  • 共享元素转换:咱们构建了一个组件,该组件由Android和iOS上的本机共享元素代码支持。这甚至能够在Native和React Native屏幕之间使用。
  • Lottie:经过在Android和iOS上包装现有库,咱们可以让Lottie在React Native中工做。
  • 本机网络堆栈: React Native在两个平台上使用咱们现有的本机网络堆栈和缓存。
  • 其余核心基础知识:就像网络同样,咱们包装了其他的现有本地基础设施,如i18n,实验等,以便它在React Native中无缝运行。

动画
感谢React Native Animated库,咱们可以实现无抖动的动画甚至是交互驱动的动画,例如滚动视差。

JS / React开源 由于React Native真正运行React和javascript,因此咱们可以利用极大的javascript项目,如redux,reselect,jest等。

Flexbox的
React Native使用Yoga处理布局,这是一个跨平台的C库,可经过flexbox API 处理布局计算。在早期,咱们受到瑜伽限制的影响,例如缺少宽高比,但它们已在后续更新中添加。此外,有趣的教程,如flexbox froggy使入门更加好玩。

与Web协做
在React Native探索的后期,咱们当即开始为web,iOS和Android构建。鉴于Web也使用Redux,咱们发现大量代码能够在Web和本机平台之间共享而无需更改。

缺点

React Native
React Native不如Android或iOS成熟。它更新,更雄心勃勃,移动速度极快。虽然React Native在大多数状况下都能很好地运行,但有些状况下,它的不成熟表现出来而且在本地调试某一些bug很难解决。不幸的是,这些实例很难预测,可能须要几个小时到几天才能解决。

维护React Native的分支
因为React Native的不成熟,咱们有时须要修补React Native源。除了回馈React Native以外,咱们还必须维护一个fork,咱们能够快速合并更改并突破咱们的版本。在这两年中,咱们不得不在React Native之上添加大约50个提交。这使得升级React Native的过程很是痛苦。

JavaScript工具
JavaScript是一种无类型语言。缺少类型安全性难以扩展,也成为移动工程师争论的焦点,由于他们习惯于输入可能对学习React Native感兴趣的语言。咱们探索了采用流程,但隐秘的错误消息致使使人沮丧的开发人员体验。咱们还研究了TypeScript,可是将它集成到咱们现有的基础设施中,例如babel和metro bundler,这被证实是有问题的。可是,咱们正在继续积极研究网络上的TypeScript。

重构
JavaScript没法解决的反作用是重构很是困难而且容易出错。重命名道具,尤为是具备通用名称的道具,如onClick或经过多个组件传递的道具,这些都是精确重构的噩梦。更糟糕的是,重构在生产中而不是在编译时中断,而且很难添加适当的静态分析。

JavaScriptCore不一致
React Native的一个微妙而棘手的方面是因为它是在JavaScriptCore环境中执行的。如下是咱们遇到的后果:

  • iOS提供了本身的JavaScriptCore开箱即用。这意味着iOS大部分都是一致的,对咱们来讲没有问题。
  • Android不提供本身的JavaScriptCore,所以React Native捆绑了它本身的。可是,默认状况下你获得的以前的。结果,咱们不得不不遗余力捆绑一个最新的
  • 在调试时,React Native会附加到Chrome Developer Tools实例。这很棒,由于它是一个功能强大的调试器。可是,一旦附加调试器,全部JavaScript都在Chrome的V8引擎中运行。99.9%的时间都很好。可是,在一个实例中,咱们获得了何时toLocaleString在iOS上工做,但只在调试时才适用于Android。事实证实,Android JSC 不包含它,它默默地失败,除非你在调试,在这种状况下,它使用的是V8。在不知道这样的技术细节的状况下,它可能会致使产品工程师经历数天的痛苦调试。

React Native开源库
学习平台既困难又耗时。大多数人只知道一两个平台。具备本地桥(例如地图,视频等)的React Native库须要全部三个平台的相同知识才能成功。咱们发现大多数React Native Open源项目都是由只有一两个经验的人编写的。这致使Android或iOS上出现不一致或意外错误。

在Android上,许多React Native库还要求您使用node_modules的相对路径,而不是发布与社区所指望的不一致的maven工件。

并行基础设施和功能工做
咱们在Android和iOS上积累了多年的原生基础设施。可是,在React Native中,咱们从一个空白的平板开始,不得不编写或建立全部现有基础架构的桥梁。这意味着有时候产品工程师须要一些尚不存在的功能。在那时,他们要么必须在他们不熟悉的平台上工做,要么在他们的项目范围以外进行构建,要么被阻止直到能够建立它。

APP崩溃监控
咱们使用Bugsnag在Android和iOS上进行崩溃报告。虽然咱们可以让Bugsnag在两个平台上都能正常工做,但它的可靠性较低,须要的工做量比其余平台要多。由于React Native在业界相对较新且不多见,因此咱们必须构建大量的基础设施,例如在内部上传源映射,而且必须与Bugsnag一块儿工做,以便可以执行过滤崩溃等事情。反应原生。

因为React Native周围的自定义基础架构数量不少,咱们偶尔会遇到严重的问题,即未报告崩溃或源地图未正确上传。

最后,若是问题跨越React Native和本机代码,调试React Native崩溃一般更具挑战性,由于堆栈跟踪不会在React Native和native之间跳转。

桥接
React Native有一个桥接API,用于在本机和React Native之间进行通讯。虽然它按预期工做,但编写起来很是麻烦。首先,它须要正确设置全部三个开发环境。咱们还遇到了不少问题,其中来自JavaScript的类型是出乎意料的。例如,整数一般用字符串包裹,这个问题直到经过桥接才会实现。更糟糕的是,有时iOS会在Android崩溃时无声地失败。咱们开始研究从2017年末开始自动生成TypeScript定义的桥接代码,但实在太晚了。

初始化时间
在React Native第一次呈现以前,必须初始化其运行时。不幸的是,即便在高端设备上,这对于咱们的应用程序来讲也须要几秒钟。这使得使用React Native用于启动屏幕几乎是不可能的。咱们经过在app-launch处初始化它来最小化React Native的首次渲染时间。

初始渲染时间
与原生屏幕不一样,渲染React Native须要至少一个完整的主线程 - > js - >瑜伽布局线程 - >主线程往返才有足够的信息来首次渲染屏幕。咱们在iOS上看到平均初始p90渲染时间为280毫秒,而在Android上则为440毫秒。在Android上,咱们使用了postponeEnterTransition API,它一般用于共享元素转换,以延迟显示屏幕,直到它呈现为止。在iOS上,咱们遇到了从React Native快速设置导航栏配置的问题。所以,咱们在全部React Native屏幕转换中添加了50ms的人为延迟,以防止导航栏在加载配置后闪烁。

应用大小
React Native对应用程序大小的影响也不容忽视。在Android上,React Native(Java + JS +本地库,如Yoga + Javascript Runtime)的总大小为每一个ABI 8mb。在一个APK中使用x86和arm(仅32位),它将更接近12mb。

64位
因为问题,咱们仍然没法在Android上发送64位APK 。

手势 咱们避免将React Native用于涉及复杂手势的屏幕,由于Android和iOS的触摸子系统不一样,所以提出统一的API对整个React Native社区来讲都是一个挑战。可是,工做正在继续进行,而react-native-gesture-handler只是达到1.0。

很长的list React Native在这个领域取得了一些进展,包括像FlatList这样的库。然而,它们远不及Android 上的RecyclerView或iOS 上的UICollectionView的成熟度和灵活性。因为线程化,许多限制很难克服。没法同步访问适配器数据,所以能够在快速滚动时异步呈现视图时查看视图。文本也没法同步测量,所以iOS没法使用预先计算的单元格高度进行某些优化。

React Native的升级 虽然大多数React Native升级都是微不足道的,但也有一些使人痛苦。特别是,几乎不可能使用React Native 0.43(2017年4月)到0。49(2017年10月),由于它使用了React 16 alpha和beta。这是一个很是大的问题,由于大多数专为Web使用而设计的React库不支持预发布的React版本。在这次升级中纠缠正确的依赖关系的过程对2017年中期其余React Native基础架构的工做形成了重大损害。

麻烦的崩溃
咱们不得不处理一些难以解决的很是奇怪的崩溃事件。例如,咱们目前正在经历@ReactProp注释的崩溃,而且没法在任何设备上重现它,即便那些具备相同硬件和软件的设备也会在野外崩溃。

Android上的进程中保存的实例状态 Android常常清理后台进程,但让他们有机会同步保存本身的状态。可是,在React Native上,只能在js线程中访问全部状态,所以没法同步完成。即便不是这种状况,redux做为状态存储也不兼容这种方法,由于它包含可序列化和非可序列化数据的混合,而且可能包含的数据超过了savedInstanceState包中可能致使崩溃的数据。生产。

相关文章
相关标签/搜索