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.

因此,對於很多其他企業(PinterestInstagram等等)而言,React Native 仍然是不錯甚至最好的選擇

 

參考資料

 

有所得、有所惑,真好

關注「前端向後」微信公眾號,你將收穫一系列「用原創」的高品質技術文章,主題包括但不限於前端、Node.js以及服務端技術

本文首發於 ayqy.net,原文鏈接 //www.ayqy.net/blog/react-native-at-airbnb/