- 原文地址:Things about React Native I found the hard (but rewarding) way
- 原文做者:Christos Sotiriou
- 译文出自:掘金翻译计划
- 本文永久连接:github.com/xitu/gold-m…
- 译者:jerryOnlyZRJ
- 校对者:xilihuasi,WangLeto
React Native 已经存在了一段时间了。当它支持 Android 的版本(iOS 以后大约一年)发布后,我便使用它进行专业级开发了,我决定投入时间在 RN 上进行跨平台开发。当我发现 React Native 时,我从事 iOS 开发工做已经六年了,并且不只仅是 Mac OS X 的开发人员。css
我已经在 App Store 和 Play Store 为个人客户开发了四个中等大小(一万到两万左右行代码,不包括依赖项)的项目。我还在一个使用 React Native 编写的代码超过五万行(除了本机代码)的大型项目中参与监督和贡献,如今已经部署上线并运行顺利。我已经积累了足够的经验来找出 React(和 React Native)闪耀的地方,和解决它短处的方案。前端
注意:我知道大家中的一些人在阅读这篇文章时会提到 Flutter。但因为它的成熟度远不及它的竞争对手,因此我尚未深刻了解它。react
在撰写本文时,React Native 的当前稳定版本为 0.57,即将到达 0.58RC。android
这是个人想法:ios
React Native 最广为人知的特性是跨平台,但这并非它引发我注意的缘由。React Native 最重要的特性是它使用 React,从而支持通用的声明式布局。跨平台支持排在重要性的第二位。做为一名 iOS 开发人员,我一直在尝试那些不那么直观的用户界面设计方式的 Auto Layout 系统(指 IOS 开发使用的自动布局系统)。git
若是你的系统具备高度动态性,而且屏幕上的元素相互依赖(好比一些侧边栏和动画),那么使用 Apple 的 Autolayout 是管理屏幕上内容的最佳方式。可是,对于大多数 Android 和 iOS 应用程序,状况并不是如此。大多数 Android 和 iOS 应用程序都会使用咱们常常看到的标准元素:文本、按钮、列表、通用视图和图像并以最相似于 Web 的方式布局。github
在 AutoLayout 出现的同时,flex 布局也被发明出来,并成为了屏幕上元素布局约定俗成的标准。除了标准的 Web 使用以外,还有一些布局系统旨在利用 FlexBox 原则进行原生开发:数据库
这只是比较出名的一些,还有不少 UI 库。他们有一个共同点,就是他们都使用了声明式 UI。编程
建立移动开发的声明式 UI 是为了解决传统布局系统所具备的问题。你声明了你的意图,系统会根据它们生成结果。后端
声明式 UI 解决的一些移动开发挑战以下:
组件化。iOS 使用 ViewControllers 中嵌套 ViewControllers 或者 views 中嵌套 views,而 Android 使用 Fragments。二者都具备 XML 接口声明,而且都容许运行时实例化和编辑视图。当涉及将它们分解为较小的模块或复用它们时,你就是在进行一个小型的重构。在声明式 UI 中,你随时均可以对模块或者组件进行复用。
开发人员的生产力。声明式 UI 负责为你调整组件大小。看看这段代码(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 的友好体验。我看到的每一个声明式 UI 都能更好。
在 iOS 和 Android 上,若是你有大的单片 UI,那你就作错了。可是,大型 XML 文件在大多数状况下是不可避免的(请注意 iOS:XIB 其实是 XML 文件)。它们的变化对代码审查者(或你)没有任何意义,若是你不一样意以前的版本(你的更改或其余开发人员)完整保留,则发起 Pull Request 几乎是不可能的。
使用 React 和其余声明性 UI 库,这些问题在很大程度上被最小化,由于布局是实际代码,你能够更新、删除、合并、对比差别以及执行全部你你平时对其余软件执行的操做。
你可能须要成为移动开发人员才能掌握性能的概念,并管理有效的内存和数据处理器使用。
Web 开发人员能够在不了解原生的状况下使用 React Native 开发,这种说法仅适用于小型项目。一旦应用程序开始增加而且 Redux 的 store 的计算开始对应用程序的性能形成影响时,你将须要了解原生端如何工做的,才能理解为何会这样。你还须要意识到 React Native 中的 Redux 的 Store 致使的从新渲染与 DOM 中发生的从新渲染并不彻底相同,尤为是应用中的原生组件。
同时,在 React Native 上从新渲染应用程序的组件会变得代价昂贵。因为 React Native 本质是使用 bridge,所以你在 render()
函数内部提供的任何指令都将从 JavascriptCore 传递到 Java 或者 Objective C++。原生端将获取 render()
给出的 JSX 标签,并将它们转换为其原生对应部分,例如视图、标签和图像。若是转换每秒进行数百次,那就须要不可忽略的 cpu 时间。
在性能方面,彷佛 React Native 是更好的跨平台解决方案之一。可是,在某些关键领域 React Native 仍然存在性能问题。
一个这样的例子是大型数据集(和列表)。在存在大型列表和网格视图的状况下,Android 和 iOS 都提供了一个出色且极其灵活的解决方案 —— 回收视图。想象一下,当使用大型列表视图(不管是 iOS 或是 Android)时,只渲染在任何给定时间显示的单元格。其余单元格被标记为可重复使用,以便在即将显示新单元格时能够重复使用它们。更改数据集时,操做系统只需更新显示的单元格。
React Native 为大型数据集提供 VirtualizedList 及其派生的(FlatList 和 SectionList)。然而,即便这样也有不少不足之处。存在性能开销,尤为是在 SectionList 中渲染复杂组件并尝试更新一百多个对象的大型数据集时。更新机制会使低端或中端移动设备缓慢运行。
为了解决这个问题,我已经从 Redux 切换到 MobX,它为个人组件提供了更可预测的更新。此外,在大型列表的状况下,MobX 能够更新特定单元格而无需从新呈现整个列表。一般这也能够经过 Redux 实现,可是你须要重写 componentShouldUpdate()
方法并编写更多样板文件以免没必要要的从新渲染。在将其他变量复制到新状态时,你的 reducer 仍会执行一些没必要要的工做。
写在最后:总之要当心。若是你正在使用 React Native,要想让你的应用程有最好的表现效果,就要对 React 和原生的最佳实践都很熟悉。
能够经过将调试信息发送到 Chrome 对 React Native 进行调试。这意味着在设备中运行实际代码的过程与你调试代码的过程不一样。
Android 和 iOS 上的 React Native 使用 JavascriptCore 执行 Javascript。可是,调试工具在 V8(Chrome)上运行。为了使系统有更广泛适用性,在撰写本文时,React Native 在 iOS 上使用 Apple 的 Javascript Core,而在 Android 上,他们使用的是已经发布三年的 JS Core 来构建脚本(由于 Android 没有提供任何像 iOS 这样现成的 JS 运行时,Facebook 也必须本身构建)。这就致使缺少了不少 JS 新特性,好比 Proxy 对象只在 Android 上和 iOS 64 位上支持。所以,若是你想使用 MobX 5+,那你必须使用升级的 Javascript 运行时(继续阅读以了解如何作到这一点)。
运行时差别一般会致使错误只能在生产模式中重现。更糟糕的是,甚至有些错误会变得难以调试。
例如,当涉及 React Native 时,移动端数据库的最佳解决方案是 Realm。可是,当进入调试模式时,会发生这种状况:github.com/realm/realm…。虽然 Realm 的研发人员已经解释了为何会这样,但最重要的是,若是咱们想要一个更稳定的调试解决方案,必须改进 React Native 的调试架构。好消息是我一直在使用 Haul 做为个人捆绑包,它容许我直接从个人 iOS 设备进行调试,而无需经过 Chrome Dev Tools(不幸的是,你须要 Mac、iOS 模拟器和 Safari)。
请注意,Facebook 上的人已经发现了这个问题,他们正在从新设计 React 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 Navigation,它们甚至都不是问题。深层的连接和导航跳转能让用户感受天然而流畅。虽然还有其余导航库,但 React Navigation 被认为是事实上的标准。你应该试一试,这是 React Native 比 iOS 和Android 更好的地方。
与任何其余技术同样,你须要在投入使用前了解它能作什么不能作什么。如下是 RN 在哪些类别的应用上具备优点:
这里还列出了一些对于 RN 来讲表现不算太好的一些类别的应用:
确实,对于 React 没法作到的事情,你能够在原生中编写所需的全部内容,而后从 React Native 调用相应的代码。但这意味着你须要为每一个平台(iOS、Android)编写一次代码,而后为 Javascript 接口编写额外的代码。
React Native 的内部组件目前正在经历一个主要的重构,以让 RN 能够并行执行更多同步操做,以便它能够与原生共享公共代码:facebook.github.io/react-nativ…。所以,在此以前,你应该在决定是否使用它以前进行一些研究。
React Native 是一个通过深思熟虑且发展良好的平台。它为你的应用打开了 NodeJS 的世界,让你在最好的布局系统中进行编程。它还为你提供了与原生方面的良好 bridge,以便你能够充分利用这两个世界。
然而,它也属于另外一个奇怪的类别,有时候你只须要一个团队来开发你的应用程序,但有时候也须要三个!在某些时候,你须要一些 iOS 和 Android 开发人员来构建 React Native 默认状况下没有的组件。一旦你的团队开始成长,你将不得不决定是否将你的应用程序设为 100% 原生。所以,你为下一个项目是否选择 React Native,取决于你拥有多少原生代码(Java、Kotlin、Swift、ObjC)。
个人我的建议:若是你意识到你须要三个团队来开发一个应用程序的三个不一样层面(一个 iOS 团队、一个 Android 团队和一个 React 团队),那么你应该能够一直使用原生 iOS 和 Android 并抛弃 React Native 。仅维护两个代码库而不是开发三个代码库,你将节省时间和金钱。
可是,若是你拥有一个由熟练的开发人员组成的小团队并但愿构建内容应用程序或相似的软件,那么 React Native 是一个很好的选择。
若是发现译文存在错误或其余须要改进的地方,欢迎到 掘金翻译计划 对译文进行修改并 PR,也可得到相应奖励积分。文章开头的 本文永久连接 即为本文在 GitHub 上的 MarkDown 连接。
掘金翻译计划 是一个翻译优质互联网技术文章的社区,文章来源为 掘金 上的英文分享文章。内容覆盖 Android、iOS、前端、后端、区块链、产品、设计、人工智能等领域,想要查看更多优质译文请持续关注 掘金翻译计划、官方微博、知乎专栏。