首页
Preview

深入探究Airbnb的服务器驱动UI系统

如何使用名为Ghost Platform的服务器驱动UI系统在Web、iOS和Android上更快地推出功能。👻

背景:服务器驱动UI

在我们深入了解Airbnb的服务器驱动UI(SDUI)实现之前,了解SDUI的一般概念以及它如何优于传统的客户端驱动UI非常重要。

在传统的世界中,数据由后端驱动,UI由每个客户端(Web、iOS和Android)驱动。例如,让我们以Airbnb的列表页面为例。为了向我们的用户展示一个列表,我们可能会从后端请求列表数据。在接收到这些列表数据后,客户端将该数据转换为UI。

这会带来一些问题。首先,在每个客户端上构建了特定于列表的逻辑来转换和呈现列表数据。这种逻辑很快变得复杂,并且如果我们改变了列表的显示方式,它就变得不够灵活。

其次,每个客户端都必须保持一致性。如上所述,这个屏幕的逻辑很快变得复杂,每个客户端都有自己的细节和特定的实现,用于处理状态、显示UI等。客户端很容易迅速分歧。

最后,移动端存在版本问题。每次我们需要为列表页面添加新功能时,我们都需要为用户发布新版本的移动应用程序,以便他们获得最新的体验。在用户更新之前,我们很难确定用户是否使用或对这些新功能做出了良好的响应。

SDUI的案例

如果客户端甚至不需要知道它们正在显示一个列表会怎么样?如果我们可以直接将UI传递给客户端,并跳过列表数据的想法会怎么样?这就是SDUI所做的——我们一起传递UI和数据,客户端无论包含什么数据都能显示它。

Airbnb的特定SDUI实现使我们的后端能够同时控制数据及其如何在Web、iOS和Android应用程序上显示。从屏幕的布局、如何在该布局中排列各个部分、在每个部分中显示的数据,甚至是用户与部分交互时采取的操作,都由单个后端响应控制。

Airbnb的SDUI:Ghost Platform 👻

Ghost Platform(GP)是一个统一的、有意见的、服务器驱动的UI系统,使我们能够快速迭代并在Web、iOS和Android上安全地推出功能。它被称为Ghost,因为我们的Airbnb应用程序有两个方面——“Guest”和“Host”功能。

GP提供了Web、iOS和Android框架,以每个客户端的本地语言(分别为TypeScript、Swift和Kotlin),使开发人员能够使用最少的设置创建服务器驱动的功能。

GP的核心特性是功能可以共享通用部分、布局和操作库,其中许多向后兼容,使团队能够更快地推出并将复杂的业务逻辑移动到后端的中心位置。

一个标准化的架构

Ghost Platform的骨干是一个标准化的数据模型,客户端可以使用它来呈现UI。为了实现这一点,GP利用了一个统一的数据服务网格,称为Viaduct,跨后端服务使用共享的数据层。

帮助我们使服务器驱动的UI系统可扩展的关键决策是为Web、iOS和Android应用程序使用单个共享的GraphQL模式——即,我们正在使用相同的模式来处理响应并生成强类型数据模型,覆盖我们所有平台。

我们花费了时间来概括不同功能的共享方面,并以一致、周到的方式考虑每个页面的特点。结果是一个通用的模式,能够渲染Airbnb上的所有功能。该模式足够强大,可以考虑可重用部分、动态布局、子页面、操作等,客户端应用程序中相应的GP框架利用这个通用模式来标准化UI渲染。

GP响应

GP的第一个基本方面是整体响应的结构。在GP响应中用于描述UI的两个主要概念是部分和屏幕。

图1。GP和GP如何看待相同的特性的用户界面。

  • 部分:部分是GP的最基本的构建块。部分描述了一个UI组件的连贯数据,包含要显示的精确数据——已经被翻译、本地化和格式化。每个客户端将部分数据并将其直接转换为UI。
  • 屏幕:任何GP响应都可以具有任意数量的屏幕。每个屏幕描述了屏幕的布局,反过来描述了从“部分”数组中将出现的部分的位置(称为放置)。它还定义了其他元数据,例如如何呈现部分——例如,作为弹出窗口、模态窗口或全屏——以及日志数据。

图2。GP响应GraphQL模式的示例。

使用GP构建的功能的后端将实现此GPResponse(图2),并根据其用例填充屏幕和部分。Web、iOS和Android上的GP客户端框架为开发人员提供了标准处理方式,以获取GPResponse实现并将其翻译为UI,他们无需进行任何大量的工作。

部分

部分是GP的最基本的构建块。GP部分的关键特点是它们完全独立于其他部分和它们所显示的屏幕。

通过将部分与其周围的上下文解耦,我们获得了重用和重新用途部分的能力,而不必担心业务逻辑与任何特定功能的紧密耦合。

部分模式

在GraphQL模式中,GP部分是所有可能部分类型的联合。每个部分类型指定它们提供要呈现的字段。部分在GPResponse实现中接收一些元数据,并通过SectionContainer包装器提供,该包装器包含有关部分状态、日志数据和实际部分数据模型的详细信息。

图3。我们部分GraphQL模式的片段。

需要注意的一个重要概念是“SectionComponentType”。SectionComponentType控制部分的数据模型的呈现方式。如果需要,这使得可以以多种不同的方式呈现一个数据模型。例如,两个SectionComponentType TITLEPLUS_TITLE可能使用相同的后备TitleSection数据模型,但PLUS_TITLE实现将使用Airbnb Plus特定的标志和标题样式来呈现TitleSection。这为使用GP的功能提供了灵活性,同时仍然促进了架构和数据的可重用性。

图4. 使用SectionComponentType以不同的方式呈现TitleSection数据模型的示例。

Section Components

通过“Section Components”,将部分数据转换为UI。每个部分组件负责将数据模型和SectionComponentType转换为UI组件。GP在每个平台的本地语言(即Typescript、Swift、Kotlin)中提供了抽象部分组件,并可以由开发人员扩展以创建新的部分。

部分组件将部分数据模型映射到一个唯一的渲染,因此仅涉及一个SectionComponentType。如前所述,部分在没有屏幕或周围部分的上下文的情况下呈现,因此每个部分组件没有提供任何特定于功能的业务逻辑。

我是一名Android开发人员,所以让我们来看一个Android示例(因为Kotlin很棒😄)。要构建标题部分,我们有如下所示的代码片段(图5)。Web和iOS具有类似的实现——在Typescript和Swift中分别构建部分组件。

图5. 部分组件的Kotlin示例。

GP提供了许多“核心”部分组件,例如上面示例的TitleSectionComponent(图5),旨在可配置、可样式化并且向后兼容,以便我们适应任何功能的用例。但是,构建GP上的新功能的开发人员可以根据需要添加新的部分组件。

图6. GP将部分数据获取,使用部分组件将其转换为UI(来自图5的TitleSectionComponent),并将构建的部分UI呈现给用户。

屏幕

屏幕是GP的另一个构建块,但与部分不同,屏幕大多由GP客户端框架处理,并且在使用上更具有见解性。GP屏幕负责部分的布局和组织。

屏幕模式

屏幕以ScreenContainer类型接收。屏幕可以在模态(弹出窗口)、底部工作表或全屏中启动,具体取决于screenProperties字段中包含的值。

屏幕通过LayoutsPerFormFactor类型启用屏幕布局的动态配置,进而通过名为ILayout的接口指定紧凑和宽松断点的布局。下面将详细介绍ILayout。然后,每个客户端的GP框架使用屏幕密度、旋转和其他因素来确定从LayoutsPerFormFactor中渲染哪个ILayout

图7. GP屏幕模式的示例。

ILayouts

图8. ILayout实现的一些示例,用于指定各种放置方式。

ILayout使屏幕能够根据响应更改布局。在模式中,ILayout是一个接口,每个ILayout实现都指定了各种放置方式。放置包含指向响应最外层的部分的一个或多个SectionDetail类型。我们指向部分数据模型而不是内联包含它们。通过在布局配置(来自图7的LayoutsPerFormFactor)之间重用部分,这缩小了响应大小。

图9. GP的ILayout模式示例。

由于ILayout类型比部分更具见解性,因此GP客户端框架会填充ILayout,供开发人员使用。每个ILayout在每个客户端的GP框架中具有唯一的渲染器。布局渲染器从每个放置中获取每个SectionDetail,找到正确的部分组件来呈现该部分,使用该部分组件构建该部分的UI,最后将构建的UI放置到布局中。

操作

GP的最后一个概念是我们的操作和事件处理基础设施。 GP最具有改变游戏规则的方面之一是除了从网络响应中定义屏幕的部分和布局之外,我们还可以定义当用户与屏幕上的UI交互时执行的操作,例如点击按钮或滑动卡片。我们通过我们的模式中的IAction接口来实现这一点。

图10. GP IAction模式的示例:

回想一下之前(图6),部分组件是将我们的TitleSection转换为每个客户端上的UI的东西。让我们看一下相同的Android示例,其中包含在单击副标题文本时触发的动态IActionTitleSectionComponent

图11. 具有在副标题单击时触发的IAction的示例部分组件。

当用户单击此部分中的副标题时,它会触发传递给TitleSectiononSubtitleClickAction字段的IAction。 GP负责将此操作路由到为该功能定义的事件处理程序,该事件处理程序将处理触发的IAction

GP处理通用的标准操作集,例如导航到屏幕或滚动到部分。功能可以添加自己的IAction类型,并使用它们来处理其功能的独特操作。由于功能特定的事件处理程序范围限定为该功能,因此它们可以包含尽可能多的功能特定业务逻辑,从而在特定用例出现时使用自定义操作和业务逻辑的自由。

将所有内容结合起来

我们已经讨论了几个概念,因此让我们看一下整个GP响应并查看如何将其呈现以将所有内容联系起来。

图12.有效GP响应的示例JSON。

创建部分组件

使用GP的功能将需要获取实现GPResponse(图2中提到)的响应。收到GPResponse后,GP基础架构会处理解析此响应并为开发人员构建部分的过程。

回想一下,我们的sections数组中的每个部分都有一个SectionComponentType和一个section数据模型。使用SectionComponentType作为呈现部分数据模型的关键。GP会找到每个部分组件并将相应的数据模型传递给它。每个部分组件都会为该部分创建UI组件,GP会将其插入到布局下方的正确位置中。

图13. 将部分数据转换为UI。

处理操作

现在,每个部分组件的UI元素都已设置好,我们需要处理用户与部分的交互。例如,如果他们点击按钮,我们需要处理点击时采取的操作。

之前提到过,GP会将路由事件处理为其正确的处理程序。上面的示例响应(图12)包含两个可以触发操作的部分,“toolbar_section”和“book_bar_footer”。用于构建这两个部分的部分组件只需要采取“IAction”并指定何时触发它,这两种情况下都是在点击按钮时触发。

我们可以通过每个客户端上的单击处理程序来实现这一点,该处理程序将使用GP基础设施在单击事件上路由事件。

button(
  onClickListener = {
    GPActionHandler.handleIAction(section.button.onClickAction)
  }
)

设置屏幕和布局

为了为我们的用户安排一个完全交互式的屏幕,GP会查找屏幕数组以找到具有“ROOT”id(GP的默认屏幕id)的屏幕。然后,GP将找到适当的ILayout类型,具体取决于与用户使用的特定设备相关的断点和其他因素。为了保持简单,我们将使用来自“compact”字段的布局,即“SingleColumnLayout”。

然后,GP将为“SingleColumnLayout”找到一个布局渲染器,在其中会膨胀具有顶部容器(“nav”放置),可滚动列表(“main”放置)和浮动页脚(“footer”放置)的布局。

该布局渲染器将获取放置的模型,其中包含“SectionDetail”对象。这些“SectionDetail”包含一些样式信息以及要膨胀的部分的“sectionId”。GP将遍历这些“SectionDetail”对象,并使用我们之前构建的部分组件将部分膨胀到它们各自的放置中。

图14. GP Infra获取已添加操作处理程序的构建部分,将部分添加到ILayout放置中。

GP的下一步是什么?

GP只存在了一年左右,但Airbnb大多数使用的功能(例如搜索、列表页面、结帐)都是在GP上构建的。尽管使用量已经达到临界点,但GP仍处于起步阶段,还有许多工作要做。

我们计划通过“嵌套部分”实现更可组合的UI,通过我们的设计工具(例如Figma)改进现有元素的可发现性,以及对部分和放置进行所见即所得的编辑,实现无代码功能更改。

如果你对服务器驱动的UI或构建可扩展的UI系统充满热情,还有很多工作要做。我们鼓励你申请我们工程团队上的空缺职位

重新设计旅行技术讲座

服务器驱动的UI很复杂。已经花费了无数小时来创建强大的架构、客户端框架和开发人员文档,使GP能够成功。

如果你想了解SDUI和GP的更高级概述,我最近有机会在Airbnb的重新设计旅行技术讲座上发言,介绍了GP。如果你时间不够,可以跳到31分钟的位置,获得服务器驱动的UI和GP的一般概述。

特别鸣谢

特别感谢Abhi VohraWensheng MaoJean-Nicolas VollmerPranay AiranStephen HerringJasper LiuKevin WeberRodolphe CourtierDaniel Garcia-CarrilloFidel SosaRoshan GoliCal StephensChen WuNick MillerYanlin ChenSusan DangAmity Wang,以及许多幕后人员,感谢他们不懈的工作和支持GP的建设。

译自:https://medium.com/airbnb-engineering/a-deep-dive-into-airbnbs-server-driven-ui-system-842244c5f5

版权声明:本文内容由TeHub注册用户自发贡献,版权归原作者所有,TeHub社区不拥有其著作权,亦不承担相应法律责任。 如果您发现本社区中有涉嫌抄袭的内容,填写侵权投诉表单进行举报,一经查实,本社区将立刻删除涉嫌侵权内容。

点赞(0)
收藏(0)
一个人玩
先找到想要的,然后出发

评论(0)

添加评论