React Native 在 Airbnb 的起起落落

写在前面

Airbnb 早在 2016 年就上了 React Native 大船,是很具代表性的先驱布道者:

In 2016, we took a big bet on React Native. Two years later, we’re ready to share our experience with the world and show what’s next.

却在 2018 年初宣布 Sunsetting React Native

Due to a variety of technical and organizational issues, we will be sunsetting React Native and putting all of our efforts into making native amazing.

从全面拥抱 React Native 到回归 Native,在这期间发生了什么?

押宝 React Native

人力吃紧,只能另寻他途:

Back then, we recognized how important mobile was becoming to our business but simply didn’t have enough mobile engineers to reach our goals.

而新的选择就是 React Native:

We saw React Native as an opportunity to open up mobile development to more engineers as well as ship code more quickly by leveraging its cross-platform nature.

Facebook 创造 React Native 的初衷
一样,Airbnb 也希望能够借助 React Native 技术 move faster,不必为一个产品功能分平台开发两套移动端代码。但作为底线,希望引进的这项新技术也能达到 Native 既定的质量标准
事实上,React Native 也确实达到了 Airbnb 最初的预期:

Many of these features were built at a time where we simply did not have enough native engineers to achieve our goals.

并非有利而无害
,在深度应用中发现了两个难题:

  • 与 Native 复杂特性的集成:如共享元素过渡动画、视差效果、地理围栏等
  • 与 Native 现有基建的配合:如网络、实验测试、国际化等

快乐并痛着

However, its benefits didn’t come without significant pain points.

从 Airbnb 的实践经验来看,React Native 的优势在于:

  • 跨平台:进而实现三端设计语言的统一,以及 Web 与 Native 的代码高度复用
  • 切合 JS 生态:无缝接入 Redux、ESLint、Prettier、reselect、jest 等 JS 生态
  • 开发效率:无需等待编译,Flexbox 布局也更容易掌握
  • Native 扩展:任何 Native 能力都可以桥接到 React Native 中,同时,Native 现有的基础设施也能集成进来
  • 性能可接受:动画同 Native 一样流畅,体验上能够满足大多数场景,很少需要关注性能

跨平台特性带来的代码复用与三端统一的可能性是无可替代的优势,而支持 JavaScript 运行时使其得以进入 JS 生态,从而获得 Hot Reloading 等免编译体验,实现开发效率的提升

实际上, 对于广泛关注的性能问题,并没有想象中的那么慢

We frequently saw mobile engineers look at JS and think “slower than Java”. However, moving business logic and layout off of the main thread actually improves render performance in many cases.

在一些触碰到能力边界的场景下,都能通过 Native Bridge 来打破限制:

Because everything in React Native can be bridged by native code, we were ultimately able to build many things we weren’t sure were possible at the beginning such as Shared element transitions, Lottie, Native networking stack, i18n, experimentation, etc.

看起来很完美。但在另一些方面,React Native 确实也带来了不少痛楚,比如:

  • 自身成熟度不够:不如 Android、iOS 成熟,存在不确定的能力边界风险
  • JS 语言的不足:弱类型让重构变得很困难且极易出错(早期尚未提供 TypeScript 支持

  • 类库建设门槛高:编写 React Native 类库需要熟知 3 个平台,否则容易出现平台特定的问题,对开发者要求很高

  • 部分特性支持度不佳:比如笨重的 Native Bridge API
    ,早期提供的无障碍访问 API 不健全,长 List 支持不如 Native 方案成熟、灵活,手势支持、JS 运行时环境存在平台差异……甚至长期以来不支持 Android 64 位(直到 2019 年 3 月的 0.59 版本才支持 Android 64 位

  • 首屏性能硬伤:秒级的运行时初始化开销,以及几百毫秒的前置首屏渲染时间,根本无法满足闪屏等场景的性能要求

  • 额外负担:引入 React Native 意味着会让包体积增大 8~12MB,同时,由于其生态尚不成熟,开发中通常面临基建与特性迭代并行

技术自身的成熟度不够,加上(类库建设的高门槛导致的)开源生态发展缓慢,致使实际使用中为了应对需要快速打补丁的场景, 通常要维护一份自己的 React Native,而这部分维护成本不容小视

We had to maintain our own fork of React Native where we could merge fixes. For these case, a one-line fix on Android or iOS wound up taking days of figuring out how to add it to React Native, cherry picking it, then filing an issue on React Native core and following up on it over the coming weeks.

另一方面,Native 多年沉淀的基础设施(崩溃监控等)都需要在 React Native 下重新建设(要么重写,要么桥接)一套,否则开发体验与效率是跟不上的:

Because React Native is relatively new and rare in the industry, we had to build a significant amount of infrastructure.

首屏性能主要难点在于:

  • 初始化时间:初始化 React Native 运行时的开销在所难免,大型应用在即使在(2018 年的)高端设备上也需要几秒
  • 开始渲染的前置时间:先要经过 JS 线程、yoga 布局线程,取到足够的信息后才能在主线程开始渲染,这期间存在 280~440ms 的白屏时间

P.S.线程模型的限制还带来了另一些问题,比如:

Many of the limitations are difficult to overcome because of the threading. Adapter data can’t be accessed synchronously so it is possible to see views flash in as they get asynchronously rendered while scrolling quickly. Text also can’t be measured synchronously so iOS can’t make certain optimizations with pre-computed cell heights.

团队组织如何跨平台?

We learned a ton about what React Native means for an engineering organization. Adopting it is much more complex than adding a new library or pattern to an existing platform.

技术也对组织架构造成了影响,这些挑战可能比技术问题更难解决

对工程师的要求

  • 平衡三端体验:React Native 本质仍然是 Native,因此 Native 基础设施不可或缺,而平台差异依旧存在,这让平衡三端体验变得相当困难
  • 跨团队定位问题:React Native 本身还在快速发展变化中,基建与特性迭代并行,加上大家都没有太多经验,让问题定位变得异常困难,甚至搞不清楚问题应该归属于哪个团队,还是来自上游的 React Native
  • 应对跨平台的复杂度:工程师大多熟悉一两个平台,而构建或调试时可能会涉及其它平台。更糟糕的是,面临这种跨平台的复杂度,工程师可能完全不知道问题该从何查起
  • 准备三套开发环境:React Native 工程师需要具备 3 套最新的开发环境,而每套环境都不那么容易搭建、学习和保持更新,每过几周都要花几个小时去更新这些环境

React Native 并不能完全屏蔽平台差异,那么就要求工程师了解这些差异,并谨慎地平衡三端体验。而跨平台带来的复杂度直接体现在问题排查链路上,工程师可能需要跨团队、跨技术栈地定位问题

团队组织面临的挑战

团队可能面临成员体感上的一些问题,例如:

  • 态度两极分化:实践经验表明,工程师对这项技术的接受程度上存在两极分化,有视之为三端统一银弹全力支持的,也有全然拒绝一点都不愿意用的
  • 感知上的迭代速度变慢:从工程师的角度来看,如果与 Native 相比,用 React Native 开发某个特性需要 1.5 倍的时间,他仍会认为花费时间更长了,尽管事实上(多平台)总共花费的时间减少了

在混合技术栈下,团队还需要考虑一些新的问题:

  • 团队如何划分、如何协作?
  • 如何高效地跨技术栈调试?
  • 如何跨平台测试、保证代码在多平台都能正常工作?
  • 如何决定新特性该用什么技术去实现?
  • 如何招聘和分配团队资源?

事实上,这种混合的技术栈也确实对人员招聘、团队划分、技术实现、培训教学等造成了一系列影响:

  • 人员招聘:业界对企业贴上了 React Native 标签,很多工程师为此犹豫是否加入,影响人员招聘
  • 团队划分:混合的团队经常面临技术和沟通上的问题,因为 代码被拆成了两份,工程师不再熟知整个逻辑流程
    ,共享业务逻辑、模型、状态等变得很困难。虽然这些问题可以通过共享资源和代码来解决,但多数团队尚未形成这样的良好氛围

  • 平衡多技术栈:哪些代码应该由 Native 实现,哪些应该放在 React Native 里,是需要权衡的。而 工程师通常不考虑这些,偏向于用选用自己熟悉的技术栈
    ,导致一些代码不那么理想

  • 培训教学:比起 Native 10 多年的资源积淀,React Native 相关的学习资源和文档还是太少,意味着还需要在技术及内部基础设施的教学培训上投入一些资源

放弃 React Native,回归 Native

Although many teams relied on React Native and had planned on using it for the foreseeable future, we were ultimately unable to meet our original goals.

经过 2 年的实践验证, 确认 React Native 并不能完全满足最初的预期

  • Move Faster:顺利时,开发速度确实无与伦比,但各种技术上和组织上的问题大大拖慢了这种速度
  • 达到 Native 既定的质量标准:React Native 的不断成熟与实践中积累的经验带来了一些性能提升,但有些技术问题(比如初始化和首屏异步渲染)仍然充满挑战,内部外部的资源匮乏加剧了这种困难
  • 不必为一个产品功能分平台开发两套移动端代码:React Native 代码几乎都能跨平台复用,但在 Airbnb App 里这部分代码占比很小,而且需要桥接大量的基础设施,所以实际结果是要在 Android、iOS、React Native 三个平台开发,而不止 Native 双平台
  • 提升开发体验:开发体验一言难尽,编译时间上表现很好,调试体验却很糟

由于以上种种, 深思熟虑之后,Airbnb 最后决定全面放弃 React Native

When we balanced the positives against the pain points plus the current needs and resources of our Engineering organization, we decided that it wasn’t right for us anymore.

具体的,自 2018 年 6 月起,所有特性迭代不再考虑 React Native 技术,相关开源项目也不再维护,并计划将高流量业务在 2018 年底全部迁由 Native 实现,逐步去除 React Native 带来的性能负担(比如启动时的初始化时间)

React Native 启发之下的 Native 开发

虽然放弃了继续使用 React Native,但在这 2 年中,Airbnb 也受到了一些 对 Native 很有价值的启发

Today, we have a number of exciting projects in production or in the pipeline. Some of these projects were inspired by the best parts and learnings from our experience with React Native.

例如:

  • Server-Driven Rendering:动态更新
  • Epoxy Components:声明式组件定义、懒加载、基于虚拟 DOM 的更新机制
  • MvRx:线程模型
  • 编译速度提升:模块化编译

Server-Driven Rendering

服务驱动的 Native 渲染(Server-Driven Rendering):

With these frameworks, the server sends data to the device describing the components to render, the screen configuration, and the actions that can occur. Each mobile platform then interprets this data and renders native screens or even entire flows using DLS components.

用于动态更新等场景:

Server-driven rendering frameworks have already provided huge value by allowing us to experiment with and update functionality instantly over-the-air.

Epoxy Component

Epoxy
是一套声明式的 Native 组件化方案,支持 Android 和 iOS:

Epoxy is a framework that enables easy heterogeneous RecyclerViews, UICollectionViews, and UITableViews.

其中,借鉴了 React 基于虚拟 DOM 的组件更新思路:

The key to React’s performance is that those components are just a data model representation of the actual views/HTML you want to render. The component tree is then diffed and only the changes are dispatched. We built a similar concept for Epoxy.

MvRx

融合通用开发模式及一些 React 思想形成的 Android 开发框架, MvRx

MvRx is an opinionated yet flexible framework that was developed by taking common development patterns that we observed as well as the best parts of React.

编译速度

通过拆分模块来缩减编译时间:

We built infrastructure on Android and iOS to enable you to compile only part of the app that includes a launcher and can depend on specific feature modules.

为什么这么难?

至此,React Native 在 Airbnb 的故事结束了
从押宝 React Native,到遭遇技术、团队组织难题,再到权衡利弊之后决定放弃,最后转而全力投入 Native 体系,并将 React Native 的一些思路应用进去……为什么这么难?连大型企业都无法驾驭这项新技术吗?

客观地讲, Airbnb 遭遇的许多困难都源自 Native 与 React Native 的混合应用
(把 React Native 集成到现有的 Native App 中):

We integrated React Native into large existing apps that continued to move at a very fast pace. Many of the difficulties we encountered were due to the hybrid model approach we took.

Facebook 直到 2018 年 6 月
计划解决通过大规模的重构来解决混合应用中存在的各种问题

We're working on a large-scale rearchitecture of React Native to make the framework more flexible and integrate better with native infrastructure in hybrid JavaScript/native apps.

而这些问题中的很多难点都是 Airbnb 所经历过,并且与之不懈斗争的。与一些小规模企业相比,Airbnb 有能力走得更深更远,所以也遭遇了更多更大的难题:

However, our scale allowed us to take on and solve some difficult problems that smaller companies may not have had time to solve.

虽然早期信徒 Airbnb 选择了放弃,但 React Native 仍在继续高速发展,并日趋成熟:

Facebook and the broader React Native community are dedicated to making React Native work for hybrid apps at scale. React Native is progressing faster than ever.

因此,对于很多其他企业( Pinterest
Instagram
等等)而言,React Native 仍然是不错甚至最好的选择

参考资料

有所得、有所惑,真好

关注「前端向后」微信公众号,你将收获一系列「用
原创」的高质量技术文章,主题包括但不限于前端、Node.js以及服务端技术

本文首发于 ayqy.net
,原文链接  http://www.ayqy.net/blog/react-native-at-airbnb/