React Native: 回顾 Udacity 移动工程团队的使用历程

React Native: 回顾 Udacity 移动工程团队的使用历程

Udacity 的移动团队最近把咱们的应用程序中用 React Native 编写的最后一个功能移除了。css

咱们收到了不少关于 React Native 及其使用的一些问题,还有人问咱们为何中止使用 React Native。html

在本文中,我但愿可以回答这些问题中的大部分,而且重点介绍:前端

  • 咱们的相关团队的规模及人员组成?
  • 咱们为何一开始会尝试 React Native?
  • 移除 React Native 的缘由?
  • 哪些方面可行?哪些方面不可行?
  • ...以及其它 🙂

我必然不能自称是 React Native 专家。咱们团队中有的人比我更有经验,不过他们也未必可以称为专家。java

我只是想谈一谈咱们本身的经验,介绍一下在咱们这个特定的使用状况下哪些可行哪些不可行。React Native 是否适合于你的团队/项目,这彻底取决于你。而本文的做用是为你决策时提供一些额外的有用参考。node

“是否在你的团队/项目中使用 React Native 彻底取决于你”react

还须要指出的是,这里的经验和观点来自于 Udacity 的移动工程团队,并不涉及到公司其余团队。咱们的想法不表明其它使用 React/React Native,或为之构建内容的团队的观点。android


团队

第一件事,咱们的团队是什么样的?你的团队的规模,经验和组织都会对 React Native 在你的工程中的可行性产生重大影响。ios

咱们的移动工程团队覆盖了 iOS 和 Android 两个平台。git

团队规模github

引入 React Native 时

  • 1 个 iOS 开发人员
  • 2 个 Android 开发人员
  • 1 个 PM
  • 1 个 设计师

现在

  • 4 个 iOS 开发人员
  • 3 个 Android 开发人员
  • 1 个 PM
  • 1 个 设计师

在咱们使用 React Native 的约 18 个月的时间里,咱们 iOS 和 Android 两个团队都扩容了。整个团队还迎来了新的 PM。咱们还经历了多种设计和多种范式。

开发者背景

当引入 React Native 时,每一个团队对 JavaScript 和 React 编程范式的温馨度如何?

iOS

iOS 团队最初惟一的那个开发者对跳进 React Native 阵营至关认同,由于他之间已经有了大量的 JavaScript 和 web 开发经验。

如今,4 个 iOS 开发者中有 3 人在 JavaScript 和 React Native 开发中比较驾轻就熟。

Android

引入 React Native 之初,两个 Android 开发者之一对 JavaScript 比较适应。另外一个(我本身)基本没有什么 JavaScript,React 或 web 开发背景。

后来加入的另外一个 Android 开发者也没什么 JavaScript 或 web 开发经验。


应用程序

咱们的应用程序是干什么用的?

咱们的移动应用程序的目标是将 Udacity 的学习体验带到移动设备上。它们须要支持认证、内容发现、项目注册(某些状况下还会有支付)、最后还要支持对不一样的项目和内容类型的学习材料的使用。

这些应用程序仍是新的实验性功能的测试温床,皆在改进用户的总体学习进展。

代码库的规模

  • iOS: 97400 行代码(.swift,.h,.m)
  • Android:93000 行代码(xml, java, kotlin, gradle)

功能的一致性

当引入 React Native 时,这些应用程序的功能基本上是一致的。

随着项目推动,核心体验基本上保持一致,但每一个团队都有一些在特定平台上独有的“实验性”功能。

此外,因为更大的国际化需求,诸如本地化和更小的 apk 软件包大小之类的需求日益成为 Android 团队的优先事项。Android 团队还和其它地区的团队密切合做,为特定市场开发一些功能,这是 iOS 团队不须要重点考虑的。


为何/如何采用 React Native?

咱们为何要引入 React Native?

当时,咱们想要开发一个全新的只在移动设备上运行的功能。咱们但愿在两个平台上实验和快速地验证,因此跨平台是很是吸引人的。

由于这是一个新的封闭性的功能,咱们将其视为一个尝试跨平台方法的有意思的尝试。

咱们选择 React Native 的缘由以下:

  • 愈来愈可行的跨平台方案
  • 团队大部分人(2/3开发者)适应 JavaScript 和 web 开发
  • 更快的开发速度
  • 公司外其它团队的成功故事

咱们是如何引入的?

咱们最初的 React Native 功能基于一个独立的 GitHub 仓库,而后将其做为 git 子树,同时合并回 iOS 和 Android 仓库中。

这种方式支持很是快速的原型开发,若是有必要,也可让这个功能以独立产品的形式发布。

随着积累了更多的原型开发经验,咱们逐渐在 React Native 代码仓库中加入更大的第二个功能。

时间线

  • Aug 2016: 建立功能 1 的 React Native 仓库
  • Nov 2016: 功能 1 在 Android 上发布
  • Nov 2016: 功能 2 开始开发
  • Dec 2016: 功能 3 原型开发开始
  • Jan 2017: 功能 1 开发结束
  • Feb 2017: 功能 2 发布
  • Mar 2017: 功能 3 原型开发结束
  • Nov 2017: 功能 2 在 Android 上的最后一次更新
  • Dec 2017: 功能 4 做为独立应用进行原型开发。最后因为性能缘由从新用本地代码实现。
  • Feb 2018: 功能 2 在 iOS 上的最后一次更新
  • Apr 2018: 功能 1 从 Android 上移除
  • Jun 2018: 功能 2 同时从两个应用中移除

移除 React Native 的动机?

答案至关直接。

咱们移除应用程序中与 React Native 相关的最后一点代码,缘由在于剩下的惟一一个 React Native 功能已经快要下线,咱们不打算对它继续支持了。

“为何中止用 React Native 开发新功能呢”

更有意思的问题多是:为何中止用 React Native 开发新功能呢?

缘由大概有如下几条:

  1. 须要同时在两个平台上开发的功能数目降低
  2. Android 平台独有的产品请求量增长
  3. 长期维护成本的噩梦
  4. Android 团队不肯继续用 React Native

那么用什么来替代呢?

用 React Native 开发的功能被移除且再也不支持,咱们也没有替代这些功能的需求。


React Native 哪些方面用得不错?

咱们在使用 React Native 时,有哪些方面用得不错呢?

  • React Native 的入门、运行、及在两个平台上的构建都至关容易
  • 能够从 React 和 JavaScript 更大的生态系统中获取代码库和工具
  • 咱们能够在两个平台上同时原型开发功能 1
  • 在一个跨功能团队中,同一个开发者能够同时为两个平台构建功能 2 的大部分代码
  • 团队对 React Native 的共同理解更深刻了

遇到过哪些问题?

在咱们使用 React Native 的过程当中,咱们遇到了一系列的问题。其中一些来源于咱们的使用过程,一些来源于咱们的使用案例,还有一些则来自于 React Native 自己。

设计和用户体验方面的挑战

平台一致的 UI/UX

由于咱们要将几个新的屏幕集成到现有的更大的用户体验区中,咱们但愿新的 React Native 代码可以遵照本地平台的开发模式和风格。这意味着咱们在两个平台上用的不必定是同一个 UI 设计。

让 React Native 的风格和本地平台一致并非什么难事,可是你须要了解每一个代码库中的设计范式。至少,你要核对每一个平台,甚至要为每一个操做系统开发新的定制控件。

对咱们而言,常常须要接触每一个平台的开发者和设计者,以理解须要作什么,不然在两边同时使用一个风格会致使 Android 这边的用户体验与其他应用彻底不一样。

在更复杂的状况下,咱们须要编写额外的平台独有的代码,来定制每一个应用程序的用户体验。

其中一个例子是,确保一个 back/up 图标的正确行为。由于要考虑到新的 React Native 功能代码集成到已有的应用中(集成到哪里/如何集成),确保 back/up 图标和 back 按钮点击时的正确行为须要 Android 平台的本地代码,并且要在 React Native 代码库中进行 Android 特有的代码改动。

在本地设计中的改动会致使集成功能时 React Native 代码的变更

至少在一个场合中,Android 应用的导航结构变更时,咱们须要相应地更新 React Native 代码,不为别的,只是为了处理二者的集成问题。

咱们没能作到让它们独立存在,React Native 实现的功能实际上在一个 fragment 中,它先是被放在一个 BottomNavigationView 的屏幕中,而后又被移到它本身与本地其它 fragment 之间的坐标状态中。

这种平台修改须要回到独立的代码库,进行修改并更新集成,确保新的改动不会对 iOS 应用程序产生负面影响。

设备相关的问题

无论你叫它“碎片化”仍是“分散化”,咱们面临的现实是,Android 设备上有更多独特的配置须要考虑。

在不少状况下,咱们发现布局不能很好地适配到不一样尺寸的 Android 手机。咱们发现最新的 iPhone 和 Pixel 设备上能够很流畅地运行动画,可是在国际市场上更普遍使用的低端设备上是作不到的。

这固然不是 React Native 独有的问题,只不过是 Android 应用开发中共有的问题。可是,这确实是咱们须要考虑的平台相关的问题,当这类问题多起来以后,咱们不得不反思:React Native 的跨平台到底为咱们省了多少时间?

全球化增加

在咱们使用 React Native 的时间里,Android 团队对国际化愈来愈关注。咱们有数个国际化办公室,它们要求本地化和轻量化的 apk 包。

React Native 是能够作字符串的本地化的,但须要额外的设置。在咱们的案例中,它须要对不一样的仓库的代码进行改动。这增长了本地化任务的复杂度,特别是你须要其它团队辅助完成本地化时,就不是很美妙了。这使得 React Native 实现的功能的本地化频率变得更低。

咱们可以按要求减少 apk 文件大小,可是难以处理 React Native 占用的至关可观的那部分。当咱们移除了最后一个用 React Native 实现的功能以后,咱们的 apk 减小了约 10MB,这包括了命令资源(command resource)和 React Native 自己。

集成的挑战

与本地组件和导航结构的集成

根据咱们的经验,将 React Native 集成到已有的应用中时,若是是一个独立的功能,则集成是至关直接的;不过如何须要与已有组件紧密集成并通讯,则你会遇到麻烦。

咱们发现,经常须要大量的桥接代码,来实现本地组件和 React Native 组件的通讯。当咱们须要修改 React Native 组件以适应导航结构时,这部分代码也是须要更新的。

工具链/构建问题

集成 React Native 须要更新每一个应用程序的构建过程。咱们使用 CircleCI 来构建咱们的工程,它须要从新配置以支持额外的 React Native 构建步骤。

在咱们以前的一篇文章中介绍过,在 Android 这边并非那么直观。

在 Android 上发布版本时的 React Native 打包: 在 Android 发布build_engineering.udacity.com 时如何运行 React Native 打包命令行

一旦咱们的构建过程包含了所需的 React Native 任务,它让 CircleCI 的发行版构建时间增长了约 20%。

在咱们的代码库中移除最后一个 React Native 功能以后,咱们有了以下的改进:

  • CircleCI 构建时间由约 15 分钟变成了 约 12 分钟
  • apk 发布文件大小由 28.4MB 变成了 18.1MB

Android 团队还经历了 Android/Gradle 构建工具与 React Native 冲突的一些问题。最近,咱们一直在解决的一个问题是:issues with Gradle 4

iOS 团队一样存在很多问题。

配置构建工具比较痛苦,由于咱们使用 React Native 时采用的是非标准文件结构。由于咱们有多个项目仓库,咱们将 React Native 仓库置于 srcroot/ReactNative 目录下,而不少已有的构建工具却假设默认的应用文件结构为 /ReactNative/ios/ios。

此外,咱们使用过 cocoapods 来管理依赖关系,它原来是包含 React Native 的推荐方式,可是后来却再也不被推荐了。这个问题由于咱们的非标准文件结构而变得更为严重,因而咱们须要在 Podfile 中用一些很恼人的小技巧来从正确的位置读取文件。

因为 cocoapods 再也不是包含 React Native 的标准方式,因而 Podfile 的更新会依赖于社区的更新,二者不老是同步的。咱们数个版本的 css/Yoga 依赖已经更新,其 Podfile 却仍然在引用错误的版本。直到最后,咱们仍然在使用一些恶心的安装后小技巧,实际上只不过是用 sed/regex 来定位那些包含的调用代码。

最后,iOS 项目的 CI 一样是一个痛点。如今,咱们不得不添加一个 npm 依赖层,并确保在继续安装前正确地更新。这为咱们的构建时间增长了很多时间。

咱们还遇到过一个崩溃问题,由于一个版本的 npm 有 package.lock,而另外一个没有,这致使咱们在一次 React Native 升级时安装了错误的依赖版本。


React Native 自身的挑战

文档

React Native 做为一个总体发展是很是快的,然而咱们也发现它的文档有时倒是缺少的。特别是当咱们第一次采用时,咱们发现某些特定版本的文档/回答也许仅仅是相关的,也许不相关。

当前,将 React Native 集成到一个已有的工程中的文档是比较少的。这也是咱们更新 CI 构建时头痛的一个问题。

当 React Native 持续进化时,文档和社区支持已经有所改善。若是咱们是从今天开始使用,也许咱们曾经的一些问题更容易找到答案。

导航

咱们最初是从 NavigationExperimental 开始的,它不是并容易使用的导航代码库。当 React Navigation 出现以后,它迅速成为社区接受的导航,因而 NavigationExperimental 在 ReactNavigation 彻底采纳以前逐步再也不推荐使用。

尽管如此,若是不将一些东西强行组合起来,咱们是没法用 ReactNavigation 来实现一些功能的(好比:在一个当前 modal 流中推送 flow)。

性能

前面已经提过,咱们有几回都注意到了性能的问题。

咱们能够在空间足够的 iOS 和 Android 设备上实现很是漂亮的动画,可是这些动画在国际市场上更普遍使用的低端 Android 设备上却难以良好运行。

在进入到应用的 React Native 部分时的加载时间比咱们预期要长。这使得它不像是一种无缝过渡。

当原型开发独立的功能 4 时,图形渲染性能成为咱们关心的主要问题,由于 React Native 彻底比不了本地程序的性能。

滞后于本地平台

由于和 iOS 或 Android 本地应用并非同步构建,React Native 经常滞后于本地平台。它经常依赖于社区所支持的新的本地功能。

一个例子是咱们急需的对 iPhone X 的 safe area 支持。咱们最终不得不暂时不支持 SafeArea,由于这个功能很快就会出来。SafeAreaView 是一种平台相关的功能,跨平台开发者在开发相关的功能时须要考虑到。

在其它时候,React Native 在采纳新平台要求方面也是滞后的,好比在 2018 年 8 月 Android 应用 要求采用 API 26。要实现这个要求,仍然有一些未解决的问题。

不持续的更新

React Native 升级时不支持后向兼容,这点让人很是崩溃。一个例子是 React Native 在升级它的 React 库时的 PropType deprecation

除非维护本身定制的复制仓库,许多第三方库若是没有继续维护则将没法使用。


维护的挑战

代码库中 React Native 部分的维护是咱们常常要头痛的问题。如前所述,Android 常常须要额外的工做来与已有代码集成或修正 UI 问题。这使得 iOS 和 Android 在不一样的 React Native 代码库分支上再也不同步,只有这样才不会让不一样平台的开发互相拖累。

由于这种分支问题,代码开始慢慢变得发散,将它们从新一致起来所需的精力愈来愈多。因而,一个平台上的更新不会马上反映到另外一个平台上。

React Native 的改动的速度也构成挑战。由于存在不持续的改动的可能性,咱们每每不能很快地更新一个依赖关系,来增长一个新功能或修复一个 BUG。

一样,一些时候这会产生更多摩擦,从而拖慢代码维护的频率。对于一个小团队,在有限的带宽下,若是没法对 React Native 代码进行容易/快速的修复,那么代码中的问题获得修复的可能性只会愈来愈低,由于它可能牵扯额外的开发精力。

增长了 React Native 以后,咱们每每不是很清楚一个 BUG 存在于哪一个层次。究竟是两个平台都有?仍是一个平台独有?若是只存在于一个平台,那这个 BUG 来自于本地代码仍是 React Native 代码?这些问题增长了复杂性,从而拖慢了 QA 进程。

当须要在代码库的 React Native 部分中修复一个问题时,目前咱们不得不一样时考虑 iOS 和 Android,因而有可能须要同时考虑 3 个技术栈,而不是理想中的 1 个。

另外,由于团队中不是全部人都对 React Native 感到适应,可以在技术栈之间快速跳转并修复问题的人天然就更少了。


咱们是否能够作得更好?

我相信咱们面临的其中一些问题来源于咱们的特定使用案例,不过,有些问题确实是能够缓解的。

更少的代码分支

让应用程序与 React Native 仓库中的改动保持一致,咱们就能够作得更好。我相信让这些更新同步会让咱们在实现这些功能时有更强烈的跨平台开发的感受。

增长设备上的测试,特别是 Android,可有助于咱们在早期发现更多 UI/性能问题,而且在发布以前修复。在新代码编写以前修复问题,又能够进一步减小代码分支的数目。

更一致的设计

从开始就采用更具体的设计计划,有可能能够改善这些功能的本地外观。一个具体的例子是,采用与本地应用程序其他部分一致的 text/margin 值,而非在新用户体验区中使用新值,并同时应用到两个平台上。

更好地理解你的团队

团队中对 React Native 更不适应的那些成员应该更努力地适应其它技术栈。这会增长可以快速修复问题的人员的数目。


有没有使用案例更适合使用 React Native?

我不认为咱们团队有人认为 React Native 一无可取。显然,我相信有些使用案例中 React Native 会更适用。

你是否须要快速地、双平台、从头原型开发/构建一个新应用?

你是否须要实现一个外观/行为与平台无关的应用/功能?

你是否有空闲的 JavaScript 开发者用于移动设备应用开发?

若是上述这些问题的答案都是确定的,那么对你来讲 React Native 多是一个可行选项。

特别是,若是你有 JavaScript/React 背景,而且不须要太多本地代码,那么 React Native 将很是具备吸引力。它能让你无需学习两个技术栈的状况下开始构建一个移动应用。

对于一个彻底跨平台应用的重头开发,React Native 也将是一个不错的选择。


咱们还会不会用 React Native?

iOS 团队和 Android 团队有不一样的说法。

iOS

有可能。iOS 团队大致上对 React Native 开发至关开心,而且已经考虑用它来构建新功能。此外,在产品发布方面,相对于 Android 平台,咱们的 PM 对 iOS 设备上的 React Native 方案更有信心。

Android

不会。理想状况下,Android 团队将不会投入到 React Native 中去。咱们发现 React Native 组件的集成过程至关烦琐,而且其用户体验并不能在全部 Android 设备上都一样运行良好。

此外,咱们倾向于坚持使用一种技术栈,而不肯意在 Android 框架上再增长一层抽象,由于这也意味着增长潜在的 BUG。

咱们的印象是,用 React Native 为 Android 建立新功能的过程是比较快的,可是这个功能从早期阶段过渡到最终发布版本,以及长期的维护过程,每每会花费更多时间。


咱们是否会再次使用其它跨平台的方案?

做为一个团队,咱们极可能不会在短时间内投入到跨平台的开发中。iOS 团队有可能会用 React Native 来构建一些新东西,而且这只限于 iOS 设备,由于他们大致上会更享受这种开发体验。

个体而言,团队成员会继续关注 React Native,以及 Flutter。由于诸如 React Native 和 Flutter 这样的解决方案也在一直进化,咱们会持续为咱们的团队评估这些技术。

那么,这就是咱们今天所处的状态。

咱们对 React Native 如何能更好地适应咱们的团队以及路线图方面有了更好的理解。咱们能够基于这种判断来指导咱们下一步的决策,从而为团队作出正确的技术选择。

“咱们可不能够肯定地判断 React Native 是否适合于你?不能。”

咱们知道 React Native 的优势以及不足,那么咱们可不能够肯定地判断 React Native 是否适合你?

不能。

可是,咱们的经验彻底可以能够做为你的参考依据,帮助你评估 React Native 在你的项目中的可行性。


想要学习更多移动开发?

关注咱们

想了解更多构建 Udacity 的工程师和科学家,请在 Medium 上关注咱们。

想加入咱们?请 @udacity,机会多多。

移动设备上的 Udacity

[移动设备上的 Udacity | iPad, iPhone 和 Android:咱们已经将 Udacity 的课程体验带到了 iPad 和 iPhone 和 Android 上。开始学习你所需的技能吧)(https://www.udacity.com/mobile)

感谢 Aashish BansalJustin Li

若是发现译文存在错误或其余须要改进的地方,欢迎到 掘金翻译计划 对译文进行修改并 PR,也可得到相应奖励积分。文章开头的 本文永久连接 即为本文在 GitHub 上的 MarkDown 连接。


掘金翻译计划 是一个翻译优质互联网技术文章的社区,文章来源为 掘金 上的英文分享文章。内容覆盖 AndroidiOS前端后端区块链产品设计人工智能等领域,想要查看更多优质译文请持续关注 掘金翻译计划官方微博知乎专栏

相关文章
相关标签/搜索