- 原文地址: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、前端、后端、区块链、产品、设计、人工智能等领域,想要查看更多优质译文请持续关注 掘金翻译计划、官方微博、知乎专栏。