【译】关于React Native在专业用于多种规模项目后的思考

1,350 阅读12分钟

React Native已经存在了一段时间了。当Android支持发布时(iOS发布后大约一年),我把它用于专业。我决定投入时间进行跨平台开发。当我发现React Native时,我已经是iOS开发人员已经有6年了,而且不仅仅是Mac OS X开发人员。

我在App Store和Play商店为我的客户开发了四个中等大小(不包括依赖项的10,000-20,000行代码)项目。我还负责监督和贡献了一个更大的项目,其中包含50,000多行使用React Native编写的代码(除了native代码),现在正在生产环境中部署并运行顺利。我已经积累了足够的经验来找出React(和React Native)的亮点,以及它不在哪里 - 以及如何扩展它。

注意:我知道你们中的一些人在阅读这篇文章时会指向Flutter。由于它的成熟度远不及它的竞争对手,我还没有考虑它。

在撰写本文时,React Native的当前稳定版本为0.57,即将有0.58RC。

以下是我的想法:

React Native最广为人知也是最不重要的的功能

React Native最广为人知的功能是它是跨平台的,但这并不是它引起我注意的原因。 React Native最重要的特性是它使用React,这样它支持一个通用的声明性布局。跨平台支持排在第二位。作为一名iOS开发人员,我一直在努力设计用户界面的方式不那么直观;自动布局系统。

如果您的系统具有高度动态性,并且屏幕上的元素相互依赖(如抽屉和动画),那么Apple的Autolayout是管理屏幕上内容的最佳方式。但是,对于大多数Android和iOS应用程序,情况并非如此。大多数Android和iOS应用程序都使用我们习惯看到的标准元素:文本,按钮,列表,通用视图和图像以最类似于Web的方式布局。

在AutoLayout发明之间的时间里,FlexBox系统被发明并用作将事物放在屏幕上的事实标准。除了标准的Web使用之外,还有一些布局系统旨在利用FlexBox原则进行原生开发:

有更多的接口库 - 这些只是众所周知的一些。他们有一个共同点,就是他们都使用声明式用户界面方法。

声明性用户界面的情况:

创建移动开发的声明性用户界面是为了解决传统布局系统所具有的问题。您声明了您的意图,系统会根据它们生成结果。

声明性用户界面解决的移动开发挑战很少如下:

组件化。 iOS在ViewControllers中使用ViewControllers并在视图中查看。 Android使用Fragments。两者都具有XML接口声明,并且都允许运行时视图实例化和编辑。当涉及将它们分解为较小的或重用它们时,你就是在进行一个小型的重构。在声明性用户界面中,默认情况下已经具有此功能。

开发者生产力。声明性用户界面负责为您调整组件大小。看看这段代码(React Native示例):

class TestTextLabel extends React.Component {
  render() {
    return (
      <view>
        <text>This is a small text</text>
        <text>{this}</text>
      </view>
    );
  }
}

上面的代码渲染了一个只包含两个文本组件的Component。注意this.props.sampleText。如果此变量太长(例如 - 10000个字符长)会发生什么?结果将是组件将调整大小以适合整个文本。如果文本到达允许空间的末尾(屏幕,让我们说),那么视图将被剪切,用户将无法看到整个文本。你需要一个滚动视图。

class TestTextLabel extends React.Component {
  render() {
    return (
      <ScrollView style={{flex : 1}}>
        <Text>This is a small text</Text>
        <Text>{this}</Text>
      </ScrollView>
    );
  }
}

唯一改变的是添加<ScrollView>元素。如果是在iOS上,这需要进行更多工作。

协作 - Git Friendliness。我见过的每个声明性UI都更好。

在iOS和Android上,如果你有大块UI,那你就做错了。但是,大型XML文件在大多数情况下是不可避免的(对于iOS来说:XIB实际上是XML文件)。它们的变化对代码审查者(或您)来说没有任何意义 - 如果您不同意以前哪个版本(您或者其他开发人员的更改)保持完整,则拉动请求几乎是不可能的。

使用React和其他声明性UI库,这些问题在很大程度上被最小化,因为布局是实际代码 - 您可以更新,删除,合并,差异以及执行您通常对软件的任何其他部分执行的所有操作的代码。

关键是要了解“性能”究竟是什么

您可能需要成为移动开发人员才能掌握性能概念并管理有效的内存和处理器使用。

Web开发人员可以在不了解Native的情况下使用React Native的概念仅适用于小型项目。一旦应用程序开始增长并且Redux商店的计算开始对应用程序的性能造成影响,您将需要了解Native端如何工作以了解为什么会发生这种情况。您还需要意识到React Native中的Redux Store导致的重新渲染与DOM中发生的重新渲染并不完全相同。这尤其适用于来自应用的Native端的组件。

同时,在React Native上重新渲染应用程序的组件会变得昂贵。由于React Native使用桥接器,因此您在render()函数内提供的任何指令都将从JavascriptCore传递到Java / Objective C ++。native端将采用JSX标记中给出的render()指令,并将它们转换为其native对应项,如视图,标签和图像。如果每秒进行数百次,那种翻译需要不可忽略的cpu时间。

在性能部门,似乎React Native是更好的跨平台解决方案之一。但是,在某些关键领域仍然存在React Native的性能问题。

一个这样的例子是大型数据集(和列表)。在大型列表和网格视图的情况下,Android和iOS提供了一个出色且极其灵活的解决方案 - 回收视图。想象一下,当使用大型列表视图(iOS / Android)时,只渲染在任何给定时间显示的单元格。其他单元格被标记为可重复使用,以便在即将显示新单元格时可以重复使用它们。更改数据集时,操作系统只需更新显示的单元格。

React Native为大型数据集提供VirtualizedList及其派生(FlatList和SectionList)。然而,即使这样也有很多不足之处。存在性能开销,尤其是在SectionList中渲染复杂组件并尝试更新100多个对象的大型数据集时。更新机制使低端或中端移动设备运行缓慢。

为了解决这个问题,我已经从Redux切换到MobX,它为我的组件提供了更可预测的更新。此外,在大型列表的情况下,MobX可以更新特定单元而无需重新呈现整个列表。通常这也可以通过Redux实现,但是您需要覆盖componentShouldUpdate()并编写更多样板文件以避免不必要的重新渲染。在将其余变量复制到新状态时,您的reducer仍会执行一些不必要的工作。

底线:小心。您使用React Native的事实意味着从您的应用程序实现最佳性能需要熟悉React的最佳实践和Native实践。

了解JS运行时及其对您的影响非常重要。

可以通过将调试信息发送到Chrome的网桥在React Native中进行调试。这意味着在设备中运行实际代码的过程与您调试代码的过程不同。

Android和iOS上的React Native使用JavascriptCore执行Javascript。但是,调试工具在V8(Chrome)上运行。为了使系统更加分散,在撰写本文时,React Native在iOS上使用Apple的Javascript Core,而在Android上,他们使用的是3年前JS Core的构建脚本(因为Android没有提供任何JS运行时)像iOS这样的盒子Facebook必须自己构建)。这导致缺乏JS功能,如Android上的代理对象支持和64位支持。因此,如果你想使用MobX 5+他/她是运气不好,除非你使用升级的Javascript运行时(继续阅读以了解如何做到这一点)。

运行时差异通常会导致错误只能在生产中重现。更糟糕的是,有些事情会变得难以辨认。

例如,当涉及React Native时,移动数据库的最佳解决方案是Realm。但是,当进入调试模式时,会发生这种情况:https://github.com/realm/realm-js/issues/491。 Realm的人已经解释了为什么会这样 - 但最重要的是,如果我们想要一个更稳定的调试解决方案,必须改进React Native的调试架构。好消息是我一直在使用Haul作为我的捆绑包,它允许我直接从我的iOS设备进行调试,而无需通过Chrome Dev Tools(不幸的是,你需要Mac,iOS模拟器和Safari)。

请注意,Facebook上的人已经知道这个问题,他们正在重新设计React Native的核心,以便Native和React Native部分可以共享相同的内存。完成此操作后,可能可以直接在设备的JavaScript运行时上进行调试。 React Native Fabric(UI-Layer Re-architecture)

不仅如此,React Native社区现在提供了js android构建脚本,允许构建更新版本的JavascriptCore并将其嵌入到React Native应用程序中。这使Android的React Native Javascript功能与iOS相提并论,也为在Android上运行的React Native增加了64位支持铺平了道路。

使用React Native进行应用内导航非常棒

您是否开发过带身份验证的移动应用程序?如果用户收到推送通知并且必须首先通过登录屏幕,并且只有在登录后他才能看到推送通知内容屏幕,会发生什么?或者,如果您当前深深嵌套在应用程序中并希望跳转到另一个应用程序部分中的完全不同的区域作为对用户操作的响应,该怎么办?

像Native这样的问题可以通过一些努力在Native中解决。使用React Navigation,它们甚至都不是问题。与相关路线和导航跳跃的深度链接感觉自然和流畅。还有其他导航库,但React Navigation被认为是事实上的标准。你应该试一试。这是React Native比iOS和Android更好的方式。

React Native is not a silver bullet

与任何其他技术一样,您需要在投资之前了解它是什么以及它是什么。以下

  • 是关于RN擅长什么的非详尽列表:
  • 内容驱动的应用程序
  • 具有类似Web的UI的应用程序。
  • 跨平台应用程序可能需要或可能不需要快速上市时间。

这里还有一份非详尽的清单描述RN还差的很远的地方:

  • 具有巨大列表的应用程序
  • 媒体驱动的应用程序无需布局(例如:简单/小型游戏,动画,视频处理)或屏幕到屏幕的过渡。
  • CPU密集型任务。

确实,对于React无法做到的事情,您可以在Native中编写所需的所有内容,然后从React Native调用相应的代码。但这意味着您需要为每个平台(iOS,Android)编写一次代码,然后为Javascript接口编写额外的代码。

React Native的内部组件目前正在经历一个主要的重构,因此RN可以并行地同步执行更多操作,以便它可以与Native共享公共代码。 facebook.github.io/react-nativ… - 在此之前,您应该在决定是否使用它之前进行一些研究。

结论

React Native是一个经过深思熟虑且发展良好的平台。它为您的应用打开了NodeJS的世界,并让您在其中一个最好的布局系统中进行编程。它还为您提供了与Native方面的良好桥梁,以便您可以充分利用这两个世界。

然而,它也属于另一个奇怪的类别 - 你需要一个或三个团队开发你的应用程序!在某些时候,您需要一些iOS和Android开发人员来构建React Native默认情况下没有的组件。一旦您的团队开始成长,您将不得不决定是否将您的应用程序设为100%Native。因此,无论您为下一个项目选择React Native,都会成为您需要拥有多少Native代码(Java / Kotlin / Swift / ObjC)的问题。

我个人的建议:如果你意识到你需要3个团队来开发一个应用程序的三个方面(一个iOS团队,一个Android团队和一个React团队)那么你应该可以一直使用原生iOS和Android并跳过React Native 。通过仅维护两个代码库而不是开发三个代码库,您将节省时间和金钱。

但是,如果你有一个由熟练的开发人员组成的小团队,并且想要构建一个内容应用程序或类似的东西,那么React Native是一个很好的选择。