继 Airbnb 之后,Udacity 也宣布弃用 React Native!

14,505 阅读19分钟

Udacity 移动端团队最近删除了 App 中使用 React Native 语言开发的相关功能。

我们收到大量有关我们用法或 React Native 的问题以及为什么我们停止投入资源和精力在 RN 上。

在这篇文章中,我希望能够回答我们收到的大多数问题并深入了解:

  • 我们团队的规模和组成是什么?
  • 为什么我们优先考虑尝试 React Native?
  • 放弃的原因是什么?
  • 什么有用?什么没有?
  • ......还有更多[表情]

我当然不会声称自己是 React Native 的专家。我们团队中的其他人比我有更有经验,但我相信他们也不会自称专家。

我将根据我们自己的经验谈论在我们的特定情况下做什么和不做什么。至于 React Native 是否适合你的团队/项目取决于你,但希望这篇文章能够提供额外有用的数据点供你参考。

“React Native 是否适合你的团队/项目取决于你”

我还是想指出,这些经验和意见来自 Udacity 的移动工程团队,而不是其他任何人。这里的想法并不反映任何其他团队使用或构建 React 或 React Native 内容的意见。

团队

首先要搞清楚的事情。我们的团队是什么样的?团队规模,经验和所在组织有能力将 React Native 的可行性在项目中产生实际影响。

我们的移动端团队分布在 iOS 和 Android 平台上。

团队规模

最初引入 React Native 时:

  • 1 名 iOS 开发
  • 2 名 Android 开发
  • 1 名产品经理
  • 1 名设计师

到了今天:

  • 4 名 iOS 开发
  • 3 名 Android 开发
  • 1 名产品经理
  • 1 名设计师

在我们使用 React Native 长达 18 个月的过程中,我们的iOS 和 Android 团队的规模都在保持增长。

团队更由一位新的产品经理负责。

我们通过多名设计师和设计范例进行过转型。

开发背景

引入 React Native 时,使用 Javascript 和 React 范例的每个团队有多舒适?

iOS

iOS 团队的唯一开发人员之前拥有丰富的 Javascript 和 Web 开发经验,非常乐意使用 React Native 开发。

如今,四个 iOS 开发者中至少有三个习惯并适应使用 Javascript 和 React Native 技术。

Android

在引入 React Native 时,两位 Android 开发人员中有一位对 Javascript 感到满意。另一位(也就是我自己)只有很少的Javascript,React 或 web 开发背景。

后来加入团队的其他 Android 开发人员也几乎没有 Javascript 或 Web 经验。

我们的 Apps

我们的产品是做什么的?

我们的手机应用旨在为将 Udacity 学习体验带到你的手机设备上。它们支持身份验证,内容发现,程序注册(在某些情况下还有支付),最后支持各种程序和内容类型的学习材料的消费。

这些应用也是新的实验性功能和计划的试验场所,目的在于改善用户的整体学习效果。

代码库规模

  • iOS:97,400 行(.swift,.h,.m 格式)
  • Android:93,000 行(xml,java,kotlin,gradle)

同等

引入 React Native 后,应用程序的功能也非常接近。

随着时间的推移,核心体验大致相当,但每个团队都增加有或多或少的对另一个平台的经验。

此外,由于国际需求的扩大,本地化和更小的 apk size 等成为 Android 团队越来越重视的事情。Android 团队还与其他地区的团队密切合作,针对非 iOS 需要考虑的市场特定功能。

为什么以及我们如何采用 RN

我们为什么要引入?

我们开始推出全新的移动端专有功能。我们希望在两个平台上快速进行实验和验证,因此跨平台开发非常具备吸引力。

因为它是一个新的并且独立的功能,所以被认为是尝试跨平台开发的契机。

选择 React Native 有几个原因:

  • 作为跨平台解决方案提高了可行性
  • 大多数(2/3 的开发人员)团队适应 Javascript 和 Web 开发
  • 提高了开发速度
  • 来自公司外其他团队的成功经验

我们是如何引入的?

最初的 React Native 功能是在一个单独的 GitHub 仓库中构建的,并作为 git 子分支分别并入 iOS 和 Android 仓库。

这样可以实现非常快速的原型设计,并且如果需要,可以将该功能作为独立产品发布。

更多的经验是原型,最终我们在 React Native 代码库中引入了第二个更大的功能。

时间线

  • 2016年8月:为功能1创建 React Native repo
  • 2016年11月:Android 平台发布功能1
  • 2016年11月:功能2开始开发
  • 2016年12月:功能3开始原型设计
  • 2017年1月:功能1开发结束
  • 2017年2月:功能2发布
  • 2017年3月:功能3原型设计结束
  • 2017年11月:Android 更新功能2
  • 2017年12月:功能4原型作为独立应用程序。由于性能问题,最终取消了对原生的支持
  • 2018年2月:iOS更新功能2
  • 2018年4月:功能1从 Android 平台中删除
  • 2018年6月:从两个应用中删除功能2

去除的动机?

答案直截了当。

我们从应用程序中删除最后的 React Native 代码,因为唯一剩下的 React Native 开发的功能已经下线,我们不再需要支持。

“为什么我们停止投入 React Native ?”

这是一个更有趣的问题。

想到下面几点原因:

  • 同时在两个平台上开发的功能数量减少
  • Android 特定产品需求的增加
  • 对长期维护成本感到失望
  • Android 团队不倾向继续使用 React Native

我们用什么取而代之?

我们已经部署和删除的 React Native 相关功能不再需要支持,自然不需要替代者。

好的方面?

我们进军 React Native 的哪些方面进展顺利?

  • 入手 React Native 开始在两个平台上构建运行确实非常简单
  • 能够从更庞大的 React 和 Javascript 生态系统中提取使用很多现有的 librayies 和工具
  • 我们能够同时在两个平台上对功能1进行原型设计
  • 跨平台团队的单个开发人员能够同时为两个平台构建大部分功能2
  • 团队对 React Native 的共同理解有所加深

我们遇到哪些问题?

在我们使用 React Native 期间,我们遇到了许多问题。其中一些归因于我们的工作流程,一些归因于我们的用例,而另一些则归因于 React Native 本身。

设计和体验的挑战

平台一致的UI / UX

因为我们将一些新的屏幕集成到现有更大的用户体验当中,所以我们希望新的 React Native 代码能够遵循原生平台模式和现有样式。这意味着我们不一定能为两个平台使用相同的 UI 设计。

在 React Native 中确保每个平台自身的样式并不困难,但它确实需要了解每个代码库中使用的设计范例。最简单的是,这需要平台检查以及每个系统的自定义小部件。

对于我们来说,这通常需要与每个平台的开发人员和设计人员沟通,来了解所需要的内容,或者两者都使用单一样式。这通常会导致 Android 方面的体验与其他应用截然不同。

一些更复杂的情况下,需要额外的开发平台特定代码来自定义应用体验。

这样的例子之一是确保返回/前进按钮图标的适配。由于需要将新的 React Native 功能集成到现有应用当中,为确保返回/前进图标和返回按钮按下的正确行为,需要针对 React Native 代码库修改 Android 特定原生代码。

原生设计的更改可能需要更改 React Native 代码以处理集成点

不止一次,Android 应用的导航结构发生变化,这要求我们更新React Native 代码。

React Native 功能不必被隔离到自己的 Activity 中,而是必须移动到一个 fragment 中,放置在一个带有BottomNavigationView 的屏幕中,然后在它自己和其他原生 fragments 之间协调。

这种类型的平台修改需要返回到单独的代码库,进行更改,更新集成,并确保新修改也不会对 iOS 平台产生负面影响。

设备特定问题

“碎片化”也好,“多样化”也罢,不论你怎么称呼,留给我们一个不争的事实是有更多独特的 Android 设备配置需要特殊考虑。

我们多次发现布局不适应不同尺寸的 Android 手机。我们发现在最新的 iPhone 或 Pixel 设备上运行流畅的动画,在 Android 广泛使用的国际市场的低端设备上却运行不佳。

这些肯定不是唯一的 React Native 问题;这些是 Android 上常见的开发挑战,但随着平台特定检查和考虑因素的增加,我们不得不开始考虑使用 React Native 实际节省了多少时间。

全球增长

在我们使用 React Native 的过程中,国际化成为 Android 团队的一个更大的挑战。我们有几个国际办事处要求实现本地化并减少 apk 大小。

React Native 中的字符串本地化可以完成,因为它确实需要额外的设置。在我们的例子中,它需要更改单独的仓库。这增加了本地化任务的复杂性,这在寻求其他团队的本地化帮助时并不理想。这直接减少了 React Native 功能的本地化频率。

我们能够在这段时间减少我们的 apk 大小,但是包含 React Native 的代码规模相当大,我们无法解决这些问题。删除最后一个 RN 功能后,我们的 apk 减小 10M 左右,包括资源删除和 React Native 本身的剔除。

集成挑战

与原生组件和导航结构集成

根据我们的经验,如果是一个独立的功能,将 React Native 集成到现有应用程序中可能非常简单,然而如果需要与现有组件紧密集成并与之通信,则可能会遇到一些挑战。

我们发现自己经常需要大量的桥接代码实现原生和 React Native 组件之间的通信。当我们需要更改 React Native 组件适应我们的导航层次结构的位置时,相关代码至少需要一次更新。

工具/构建问题

将 React Native 所需的更新合并到每个应用的构建过程中。我们使用 CircleCI (持续集成工具)来构建我们需要重新配置的项目,以支持额外的 React Native 构建步骤。

就像前面说的那样,在 Android 方面,这并不像我们想象的那样简单。

一旦我们的构建需要更新包含所需的 React Native 任务时,CircleCI 的发布编译时间增加大约20%。

从我们的代码库中删除最终的 React Native 功能后,我们看到了以下提升:

  • CircleCI 编译时间从 15min减少到 12min左右
  • 发布 apk 大小从 28.4MB 降低至 18.1MB

Android 团队也遇到过 Android/Gradle 构建工具与 React Native 冲突的问题。最近我们一直在解决 Gradle 4 版本的问题。

iOS 团队也面临着相当大的挑战。

配置构建非常痛苦,因为我们的 React Native 的文件结构并不标准。鉴于我们独立的项目仓库,我们从 srcroot/ReactNative 拉取 React Native 代码,并且许多现有的构建工具采用了默认的 app 结构,即 /ReactNative/ios/... ios。

此外,我们使用 cocoapods 进行依赖管理,这是最初建议的融入 React Native 的方式,但在后续已被弃用。我们非标准的文件结构进一步加剧这种情况,导致我们不得不在 Podfile 中包含一些令人讨厌的黑客式操作,以便能从正确的位置读取到。

由于 cocoapods 不再是包含 React Native 的规范方式,因此 Podfile 更新依赖于要​​社区去更新,这并不总是同步的。在几个版本中,css/Yoga 依赖更新掉了,但是 Podfile 却引用了一个不正确的版本... 最后没办法,我们使用一些黑科技手段解决这种问题。

最后,iOS 项目的 CI(持续集成)也是一个痛点。我们现在必须添加一个 npm 依赖层,并确保在继续安装之前正确更新它们。这为我们的构建步骤增加大量的时间。

还有一个引发崩溃的问题,因为一个包含 package.lock 文件的 npm 版本,而另一个版本没有包含,导致我们在 React Native 升级过程中安装了不正确的依赖版本。

React Native 挑战

文档

React Native 作为一个生态一直在快速发展,我们发现文档经常跟不上。特别是在我们首次采用时,我们发现特定版本的文档/答案可能关联不上。

关于集成 React Native 到现有项目的文档在当时似乎很少。这是我们更新 CI 构建所面临挑战的一个因素。

随着 React Native 不断发展,文档和支持社区的贡献得到了改善。如果我们今天开始,我们可能会更容易找到一些问题的答案。

导航

我们最初使用的是 NavigationExperimental,它不是最容易使用的导航库。当 ReactNavigation 出现时,它迅速成为社区广泛接受的导航,并且在 ReactNavigation 真正完全成熟之前NavigationExperimental 已经被废弃。

性能

如前所述,有些时候会注意到一些性能问题。

我们能够制作一些非常漂亮的动画,这些动画在规范的 iOS 和 Android 设备上看起来非常不错,但在国际市场上比较普遍的低端 Android 设备上却表现不佳。

进入应用的 React Native 部分时,加载时间比我们想要的要长。看上去往往不像是无缝过渡。

在对独立功能4进行原型设计时,绘制渲染性能是一个非常大的问题,以致 React Native 被放弃来达到支持原生体验。

滞后于原生平台

因为它不是与 iOS 或 Android 一起构建的,所以 React Native 有时会落后于原生平台。它通常很大程度上依赖于社区来支持新的原生功能。

其中一个例子就是迫切需要为 iPhone X 设备提供 SafeArea 支持。我们最终选择在短时间内不使用 SafeArea 支持该功能。使用 SafeAreaView 是跨平台开发人员需要了解的开发兼容应用程序的平台特定功能的一个示例。

在其他时候,React Native 在采用新平台要求方面落后,例如要求在 2018 年 8 月之前针对 api 26 开发 Android 应用程序。这个需求尚有几个未解决的问题。

打破更新

React Native 的非向后兼容升级非常令人沮丧。一个例子是当React Native 升级它的底层 React 库时 PropType 被弃用。

如果不再维护我们自己的自定义分支,许多第三方库就会变得无法使用。

维护挑战

维护代码库的 React Native 部分有时对我们来说是一个挑战。如前所述,Android 通常需要额外的工作,无论是与现有代码集成还是修复 UI 适配问题。这导致 iOS 和 Android 在 React Native 代码库的不同分支上工作,因此一个平台不会减慢另一个平台的步伐。

由于这种分支,代码的分歧开始慢慢形成,并且使它们达到奇偶校验所需的努力有所增加。结果,对一个平台的更新没有立即添加到另一个平台。

React Native 的变化速度也带来了挑战。由于可能会破坏更改,因此更新依赖项以获取新功能或错误修复并不总是很快。

同样,有时这会导致冲突增加,从而减慢代码的维护速度。由于团队规模小,精力有限,如果它不是 React Native 代码中的简单/快速修复,由于可能需要额外的开发工作,它的处理可能性要低得多。

添加 React Native 之后,并不总是清楚错误存在于什么级别。它是否存在于两个平台中?还是只在一个平台上?如果仅在一个平台上,是在原生代码还是 React Native 代码中?这些问题增加的复杂性有时会减慢质量保证过程。

当需要修复代码库的 React Native 部分中的问题时,我们现在必须同时考虑iOS 和 Android,并且可能使用3个开发堆栈而不是1个。

此外,只有不到100%的团队感觉 React Native 效率很高,能够迅速进入并修复某些东西的开发人员的数量也减少了。

我们能够做些什么?

我相信我们遇到的一些问题是我们的用例所特有的,但是我们可以做些不同的事情来缓解其中的一些问题。

减少分歧

我们本可以更好地保持每个应用程序与 React Native repo 中的最新更改保持同步。我相信保持这些更新同步将有助于增强我们为这些功能开发更强的真正的跨平台开发。

在更多的设备上测试,特别是在 Android 上,这样能够在早期发现更多 UI / 性能问题,并允许我们在上线之前修复它们。通过在新需求开始工作之前解决问题,这也可以减少代码分歧的数量。

更一致的设计

从一开始就更具体的设计方案可能会改善功能的原生外观。其中一个具体示例是使用与其他原生应用程序一致的文本/边距值,而不是在新体验中选择新值并在两个平台上使用它。

更好的团队理解

对 React Native 不太熟悉的团队成员可能已经做了更多努力以适应额外的开发任务。这会增加能够快速解决代码问题的人数。

是否有我们认为更合适的用例?

我相信我们团队中没有任何人认为 React Native 没有它的优点。我当然相信 React Native 非常适合用例。

你是否需要在两个平台上快速从头开始构建/构建新应用程序?

你是否正在构建一个应用程序/功能,无论平台如何,其外观/行为都相同?

你有没有想要为移动设备贡献的备用开发周期的 Javascript 开发人员?

如果对这些问题中的任何一个回答“是”,React Native 可能是你的可行性选择。

特别是,我认为如果你有 Javascript/React 的背景,并且正在寻找构建一个不需要太多原生代码的应用程序,那么 React Native 是一个非常有吸引力的选择。它将使您能够开始在移动设备上构建,而无需学习2种不同的技术栈。

对于完全跨平台应用程序的开发,React Native 也是一个很好的选择。

我们会再次使用 React Native吗?

iOS 和 Android 团队在方面意见不一。

iOS

有可能。iOS 团队通常很高兴与 React Native 合作,并考虑使用它构建新功能。此外,在产品方面,我们产品经理对在 iOS 上运行的 React Native 解决方案比对 Android 更有信心。

Android

不会。理想情况下,Android 团队将来不会投入精力到 React Native。我们发现与 React Native 组件集成的过程很麻烦,并且感觉到所有 Android 设备的体验都不是很好。

此外,还有一种倾向于坚持单一开发技术战的感觉,而不是在Android 框架之上添加新的抽象层和可能的 bugs。

我们的感觉是,React Native 在 Android 上运行新功能的速度更快,但从早期阶段到完美版本再到长期维护需要更长的时间。

我们会再另一种跨平台解决方案吗?

作为一个团队,我们可能不会在不久的将来投入跨平台开发。iOS 团队可以使用 React Native 构建一些东西,并且仍然限定于iOS,因为他们团队通常更喜欢这种体验。

个人而言,团队成员会继续关注 React Native 和 Flutter。随着 React Native 和 lutter 等解决方案的不断发展,我们将继续为我们的团队评估它们。

这就是我们今天所处的境地。

我们对 React Native 如何适应我们的团队和路线图有了更好的理解。我们可以利用这些信息为我们团队的正确技术选择做出明智的判断。

“我们能否明确地说出 React Native 是否适合你?没有。”

我们看到了 React Native 的优点以及局限性。我们能否明确地说出 React Native 是否适合你?

没有。

但是,在评估 React Native 是否适用于你的项目时,希望我们的经验可以作为帮你带来一些额外参考点。

相关推荐: 除了Java,为什么你还需要学Kotlin? Airbnb 宣布弃用 React Native!

关于我:亦枫,博客地址:yifeng.studio/,新浪微博:IT亦枫

微信扫描二维码,欢迎关注我的个人公众号:安卓笔记侠

不仅分享我的原创技术文章,还有程序员的职场遐想

彩蛋:公众号回复关键字“面试资料”,获取 BAT 面试大牛为你准备的全套面试资料!