Trip.com 是携程面向国际市场的全新品牌,受全球化战略影响,随着商业化的脚步加快,首页业务针对产品迭代速度、迭代质量、线上效果验证等方面面临新的挑战。
首页无论在哪个 App 体系下都是主要的流量入口,迭代效率和质量,以及研发成本是我们追求和要解决的问题。
在首页业务迭代中我们遇到以下几个痛点:
1)新的业务需求需要跟版本发布:在商业化越来越快的同时,有大量的产品需求需要快速上线验证其效果,目前在一个大版本需要较长时间周期的大背景下,以及 iOS AppStore 审核机制的存在,最少也需要 1 天,最慢无限期的 Review 时间。在不断缩小发布周期的大方向下,通过动态下发的方式也是我们探索的一大方向。
2)多端的研发成本:在互联网越来越快的趋势下,如何节约人力成本,解放生产力,更好更快的业务需求迭代是我们一直的诉求,多端一致性和快速 UI 搭建也是我们一大痛点。
3)业务埋点维护性和及时性:在验证产品效果的同时,会产生很多难以维护的埋点代码,在页面越来越复杂的情况下,维护成本急剧上升,当线上埋点缺失,产品和运营无法验证产品效果必须等待下个版本搭车上线。
基于以上痛点,我们推出 FoxPage Native 平台,下文将会从方案选择,框架架构,以及对于业务与技术结合思考和实践的经验分享给大家。
名字来源:FoxPage 是 IBU (国际业务部)内部一个在线可视化制作页面的平台。我们决定基于 FoxPage 搭建 Native 平台。
一、框架
1.1、技术选型及思考
首先需要明确定位以及边界,我们需要一个怎么样的框架去解决存在的痛点。
需要的:
由于在首页场景使用,高性能和稳定性是最基本的要求;
为了不跟随版本发布,所以动态性也是要考虑的;
为了解决研发成本,多端渲染也是需要解决的问题;
不考虑的场景:
不需要处理复杂的业务逻辑;
不支持动画精细的交互场景;
不考虑多个组件的联动性;
通过梳理场景和边界使得目标清晰。我们需要一个跨平台支持动态性并且高性能 UI 渲染框架。
站在自身需求的角度,调研业界成熟的方案得出的结论如下表。
React Native:动态性高,但是学习成本和性能(加载性能、页面性能)不理想;
Flutter:谷歌的跨平台框架,性能高,但是无动态性;
通过以上的调研,我们打算用 Native 解析 JSON + Flexbox 的方式来作为最终方案。
这么做有什么优势?
1)学习成本低:Flexbox 布局方式被开发广泛接受(内部跨平台技术栈用的多的是 RN);
2)开发成本低:JSON 和 Flexbox(Yoga)都有成熟的高性能可靠的第三库直接使用,加快框架开发速度(在一个月内将框架完成并且上线);
3)兼容性强:Flexbox 完美兼容 Web 端布局的方式,FoxPage 同时支持 Web 端的 DSL 的输出;
4)自定义 &扩展强:由于自研,没有包袱,可以在设计上以最符合我们的场景来设计框架;
1.2、架构设计
如何做好架构设计,可以先了解下 Chrome 是如何完成一个 HTML 到 UI 的输出。
那么 Flutter 渲染流程是如何呢?
通过调研沉淀下我们的渲染流程:
各个模块的职责清晰且独立:
Downloader:主要负责 DSL 更新与下载。
CacheManager:顾名思义,负责 DSL 的缓存管理。
Parse:这层主要是做 DSL 解析,负责将 JSON 数据组织成节点,供下层使用。
Layout:此层职责为将 Parse 模块解析之后的数据计算布局,生成布局元素。
Draw:此层职责为将 Parse 生成节点设置 Layout 层的布局信息输出 Native 视图树并提交系统完成渲染。
遵守职责化最小原则,每个模块被赋予的职责最小化并且彼此独立,使后续的维护,可以将影响范围限制模块内部,而不影响其他模块的稳定性,增强系统维护性。也使得对模块定制优化提供基础。
下文会继续针对框架内部原理做深入介绍。
1.3、DSL 的定义
数据绑定
想象一下,在我们日常开发中,往往是数据对应一个 UI 元素的显示,需要有一定的绑定数据机制。
变量的格式为{{变量名}},如以上示例:我们的 image 组件中 url 的属性被设置为 product 对象属性中的 imageUrl 的值。
数组取值格式为{{数组[Index]}},如上,我们可以通过此方法获取 products 数组中的第一个元素的图片。
事件
在组件触发事件的时侯,我们希望能做一些自定义的事情,如跳转页面,怎么定义呢?
以上示例表示在点击事件中通过 router 中的 openURL 打开了一个新的页面。
条件判断
在某些条件成立才渲染的场景下,我们也提供了条件判断,示例:
如上示例: image 组件是否隐藏通过{{data.showActivities}} =="1"来控制。
埋点机制
我们还定义了动态埋点的一些规范,如示例:
在 image 组件中声明了点击事件,并且把需要的参数,通过 data 字段一并上传服务端。
1.4、布局
我们的目标是为了解决动态性和多端一致性,那么具备一个完备的布局能力是一个基础要求。通过调研,有以下 3 种方案可选。
1)自定义:完全自定义一套规则,实现成本高,布局效率取决于实现程度,所以这边是“中”,因为是自定义,所有通用性是三者最差的,几乎独家专属。
2)Web CSS:实现一套 Web CSS 样式集,可想而知,如果实现这样的一套系统代价是极高的,为了兼容众多的 CSS 样式,布局效率必然会下降,但是此方案通用性也是最佳,多端共享。
3)Flexbox:弹性盒子布局,从 Web CSS 子集发展而来,在 RN 已得到充分证明它的适用性,由于 Yoga 的存在,让我们在实现成本上得到下降。
Yoga 是 Facebook 基于 Flexbox 的跨平台布局引擎开源库,被用于 RN,Weex 等项目中,也证明了其高性能和可靠性。
在实际使用过程中,Yoga 在 Android 中发现了一些问题,不过我们通过定制源码完美解决,并且在实际体验下来 Yoga 完美的胜任了布局的任务。
一些布局示例:
单位定义
width、height 等实际数字单位定义中,我们定义了如下的数字单位。
1.5、DSL 解析
解析层,要做的事情比较简单,为了提高性能,并且对于相同的 DSL 模板,只会做一次解析,之后便会把结果做一个缓存,以下的流程图代表着解析流程。
1.6、视图构建
视图构建相对简单,通过解析层解析之后,每个视图组件都会 ViewNode 节点一一对应视图在虚拟视图树中的状态,包括了视图布局属性,视图属性等元素信息。
以下为 iOS 代码示例:
在 ViewNode 树准备好之后,我们会将树传递到渲染层中进行渲染操作,在渲染层中,根据 ViewNode 节点的类型,通过代理的方式,从注册的组件之中创建出视图实例,配合 Yoga 布局属性,转换到 Native 视图的映射,由系统完成最终的渲染。
1.7、数据更新
在解析渲染完成之后,关于数据流是怎么处理的呢?以下是处理流程图:
为了优化性能,我们针对 UI 元素有变化的部分做 dirty 处理,会触发 Layout 和 Draw 模块重计算和重绘。
1.8、动态更新
动态更新能力是重要的一环,在云端更新了页面布局或者样式之后,App 需要即时拉取到最新的 DSL 模板。以下是流程中的时序图:
需要注意几点:
1)App 打包需要把线上目前可用的 DSL 模板打包进 App 中,避免第一次打开 App DSL 模板未下载的时候的空窗口现象;
2)版本升级需要做好数据隔离和清除;
3)DSL 最新版本下发,需要做好 backup 与异常校验;
通过动态更新机制,改变了我们发布需要跟随版本的痛点,有问题,修复之后可以直接下发到用户的 App。
1.9、可视化页面搭建平台
看到这里的看官,心中肯定会有疑问?你们提供变量、布局、事件、埋点追踪,条件,手动来写这很模板复杂度肯定是很高的,不是普通人可以胜任的吧。
是的,我们也意识到了此问题,所以配套了一套可视化的编辑界面。如下面示例图:
左边是可视化编辑页面,右侧为实际在 App 场景的使用效果,可以看出还原度还是很高的。
属性编辑界面:
二、页面工程化的转变
通过动态化的转变之后,首页的业务需求开发的工程模式与研发流程也由此发生变化。
在旧模式下,研发人员更加关注业务需求如何实现,首页的业务需求如何在已有的框架体系之内跑起来。新模式下,研发人员更注重的是,业务组件如何设计,如何完成的一个高质量的业务组件。
将研发人员关注的复杂度从面降为点,使得首页的各个业务模块之间的独立性更高,以及更加稳定。模块之间的复用性提升,如其他业务部门也想用广告组件,他们只需要在其页面做些简单的配置。
在组件生态不断补充的未来,各个业务线之间共享彼此的组件模块,想完成一个新的业务或者产品,只需要像乐高积木一样堆砌即可。
三、构建业务运营闭环
在提供技术基础的条件下,我们继续思考技术和业务之间的关系,如何将业务价值最大化,UI 搭建可以通过平台搭建,是不是可以把产品运营同学也一起参与进来,构建一个业务运营闭环。
1)产品运营同学提出需求;
2)研发人员介入需求开发,开发组件;
3)组件搭建业务上线之后,一站式追踪线上业务价值;
4)根据平台的数据来实时进行运营策略,如修改页面模块,下线模块,添加模块等等;
5)然后反推产品同学提出更合理的产品需求;
如何优化链路,科学的运营体系构建运营业务闭环也是重中之中,并且未来会持续不断在此方向上探索。
四、总结
FoxPage Native 平台上线短短 2 个月,承载了 30+ 次的业务调整,5+ 次临时调整埋点的需求,这是之前做不到的。并且合理系统的设计也增加了框架稳定性,上线至今无任何异常发生。在保证的高质量的交付的同时,也大大减少我们研发成本,如一个复杂的展示模块开发,从原来的双端 4 人日降低到双端 1 人日。
在首页动态化的探索中,遇到了很多的挑战,也有很多收获,后续有很多的功能和需求还需要继续优化和完善,并且需要考虑更多的场景支持。我们相信这是一个好的开头。
作者介绍:
马增翼,高级软件工程师,目前负责 Trip.com App 公共业务研发和迭代,专注跨平台开发技术研究。热爱和拥抱开源社区。
本文转载自公众号携程技术中心(ID:ctriptech)。
原文链接:
评论