- 原文地址:What’s Next for Mobile at Airbnb:: Bringing the best back to native
- 原文做者:Gabriel Peal
- 譯文出自:掘金翻譯計劃
- 本文永久連接:github.com/xitu/gold-m…
- 譯者:ALVINYEH
- 校對者:DateBro
這是系列博客文章中的第五篇,本文將會概述使用 React Native 的經驗,以及 Airbnb 移動端接下來要作的事情。html
即便當初在嘗試使用 React Native 時,咱們也同時加快了原生的開發。今天,咱們在生產環境或正在進行中的項目方面,有許多使人激動的計劃。其中一些項目的靈感,來自咱們使用 React Native 的最佳部分和經驗。前端
即便咱們已再也不使用 React Native,但也看到了只編寫一次產品代碼的價值。咱們仍然很是依賴通用設計語言系統(DLS),由於許多頁面在 Android 和 iOS 上幾乎如出一轍。react
幾個團隊已經嘗試開始在強大的服務器驅動的渲染框架上達成一致。使用這些框架,服務器將數據發送到設備,描述須要渲染的組件,頁面配置以及可能發生的操做。而後,每一個移動平臺都會對這些數據進行解析,並使用 DLS 組件渲染原生頁面,甚至是整個流程。android
服務器驅動的大規模渲染還有不少難題。下面是咱們正在解決的幾個問題:ios
服務器驅動的渲染框架已經提供了巨大的價值,咱們能夠即時實驗和更新功能。git
2016 年,咱們開源了 Android 的 Epoxy。Epoxy 是一個框架,能夠實現簡單的異構 RecyclerView、UICollectionView 和 UITableView。今天,大多數新頁面都採用了 Epoxy。這可讓咱們將每一個頁面拆分爲獨立的組件,實現延遲渲染。現今,咱們在 Android 和 iOS 上都有用 Epoxy。github
在 iOS 上大概長這個樣子:後端
BasicRow.epoxyModel(
content: BasicRow.Content(
titleText: "Settings",
subtitleText: "Optional subtitle"),
style: .standard,
dataID: "settings",
selectionHandler: { [weak self] _, _, _ in
self?.navigate(to: .settings)
})
複製代碼
在 Android 上,咱們利用使用 Kotlin 編寫 DSL,使編寫組件更加簡單和類型安全:安全
basicRow {
id("settings")
title(R.string.settings)
subtitleText(R.string.settings_subtitle)
onClickListener { navigateTo(SETTINGS) }
}
複製代碼
在 React 中,利用 render 可返回一個組件列表。React 性能的關鍵在於,這些組件只表示你要渲染的實際視圖/HTML 的數據模型。而後對組件樹進行擴展,只渲染更改的部分。咱們爲 Epoxy 創建了一個相似的概念。在 Epoxy 中,你能夠在 buildModel 中爲整個頁面聲明模型。與優雅的 Kotlin 和 DSL 搭配使用,在概念上與 React 很是類似,看起來像這樣:bash
override fun EpoxyController.buildModels() {
header {
id("marquee")
title(R.string.edit_profile)
}
inputRow {
id("first name")
title(R.string.first_name)
text(firstName)
onChange {
firstName = it
requestModelBuild()
}
}
// 其他模塊代碼放在這裏...
}
複製代碼
每當數據發生變化時,你都要調用 requestModelBuild()
,這個方法會從新渲染你的頁面,並調用最佳的 RecyclerView。
在 iOS 上大概長這個樣子:
override func itemModel(forDataID dataID: DemoDataID) -> EpoxyableModel? {
switch dataID {
case .header:
return DocumentMarquee.epoxyModel(
content: DocumentMarquee.Content(titleText: "Edit Profile"),
style: .standard,
dataID: DemoDataID.header)
case .inputRow:
return InputRow.epoxyModel(
content: InputRow.Content(
titleText: "First name",
inputText: firstName)
style: .standard,
dataID: DemoDataID.inputRow,
behaviorSetter: { [weak self] view, content, dataID in
view.textDidChangeBlock = { _, inputText in
self?.firstName = inputText
self?.rebuildItemModel(forDataID: .inputRow)
}
})
}
}
複製代碼
最近使人很是激動的進展之一是,咱們正在開發新架構,內部稱之爲 MvRx。 MvRx 結合了 Epoxy、Jetpack、RxJava 的優勢,以及 Kotlin 與 React 的許多原理,構建出的新頁面比以往任什麼時候候都更容易、更流暢。它是一個執拗己見而又靈活的框架,經過採用咱們觀察到的共同開發模式以及 React 的最佳部分而開發出來的。同時它也是線程安全的,幾乎全部事情都從主線程運行,這使得滾動和動畫都能變得很是流暢。
到目前爲止,它已經在各類頁面上正常工做了,而且幾乎不用去處理生命週期。咱們目前正在針對一系列 Android 產品進行試用,若是它能繼續取得成功,咱們會計劃開源。這是建立發出網絡請求的功能頁面所需的完整代碼:
data class SimpleDemoState(val listing: Async<Listing> = Uninitialized)
class SimpleDemoViewModel(override val initialState: SimpleDemoState) : MvRxViewModel<SimpleDemoState>() {
init {
fetchListing()
}
private fun fetchListing() {
// 這會自動觸發請求並將其響應映射到 Async <Listing>
// 這是一個密封類,能夠是:Unitialized、Loading、Success 和 Fail。
// 無需單獨處理成功和失敗的回調!
// 此請求也是有生命週期的。它將在配置更改後繼續存在
// 在 onStop 以後不會再傳遞。
ListingRequest.forListingId(12345L).execute { copy(listing = it) }
}
}
class SimpleDemoFragment : MvRxFragment() {
// 這將自動同步 ViewModel 狀態並重建 Epoxy 模型
// 任什麼時候候都會發生變化。相似於 React 的渲染方法:如何爲每次更改而運行
// 參數或狀態。
private val viewModel by fragmentViewModel(SimpleDemoViewModel::class)
override fun EpoxyController.buildModels() {
val (state) = withState(viewModel)
if (state.listing is Loading) {
loader()
return
}
// 這些 Epoxy 模型不是視圖自己,因此調用 buildModels 花銷很小。
// RecyclerView diffing 將自動完成,只有模型的改變纔會從新渲染。
documentMarquee {
title(state.listing().name)
}
// 其他模塊代碼放在這裏...
}
override fun EpoxyController.buildFooter() = fixedActionFooter {
val (state) = withState(viewModel)
buttonLoading(state is Loading)
buttonText(state.listing().price)
buttonOnClickListener { _ -> }
}
}
複製代碼
MvRx 的架構比較簡單,主要用於處理 Fragment 參數,跨進程重啓的 savedInstanceState 持久性,TTI 跟蹤以及其餘一些功能。
咱們還在開發一個相似的 iOS 框架,該框架正在進行早期測試。
預計很快會聽到更多這方面的消息,咱們對迄今取得的進展感到興奮。
當從 React Native 切換回原生時,立刻顯現出來的問題就是迭代速度。從一個在一或兩秒就能可靠地測試更改部分的平臺,到一個可能須要等待 15 分鐘的平臺,根本沒法接受。幸虧,咱們也找到了一些補救措施。
咱們在 Android 和 iOS 上構建了基礎架構,能夠只編譯包含啓動器的應用中的一部分,而且能夠依賴於特定的功能模塊。
在 Android 上,這裏使用了 gradle product flavors。咱們的 gradle 模塊看起來像這樣:
這種新的間接層,使得工程師們可以在應用的一小部分上進行構建和開發。與 IntelliJ 的卸載模塊配合使用,大大提升了 MacBook Pro 上的構建時間和 IDE 性能。
咱們編寫了腳原本建立新的測試 flavor,在短短几個月內,咱們已經建立了 20 多個。使用這些新的 flavor 開發版本平均要快 2.5 倍,花費 5 分鐘以上的構建時間百分比降低了 15 倍。
做爲參考,這是 gradle 代碼段,可用於動態生成具備根依賴性模塊的 product flavor。
一樣,在 iOS 上,咱們的模塊以下所示:
相同系統的構建速度可提升 3-8 倍
很高興可以成爲一家不怕嘗試新技術,同時又努力保持高質量、高速度和良好開發體驗的公司。最後,React Native 是一個發行新功能的重要工具,它爲咱們提供了新的移動開發思路。若是你想參與其中,請告訴咱們!
這是系列博客文章的第五部分,重點講述了咱們使用 React Native 的經驗,以及 Airbnb 移動端接下來要作的事情。
若是發現譯文存在錯誤或其餘須要改進的地方,歡迎到 掘金翻譯計劃 對譯文進行修改並 PR,也可得到相應獎勵積分。文章開頭的 本文永久連接 即爲本文在 GitHub 上的 MarkDown 連接。
掘金翻譯計劃 是一個翻譯優質互聯網技術文章的社區,文章來源爲 掘金 上的英文分享文章。內容覆蓋 Android、iOS、前端、後端、區塊鏈、產品、設計、人工智能等領域,想要查看更多優質譯文請持續關注 掘金翻譯計劃、官方微博、知乎專欄。