在 iOS 和 Android 系统近期推送的更迭版本中,系统环境已经逐渐发展出了将部分内容和服务前置化展示的趋势。
同时,伴随着富媒体内容井喷式增长以及内容的多样化、年轻化,一款移动应用如何能有效提升动态化运营效率,也成为了各行业产研与运营的重要课题。
在上述技术能力和行业需求的双重背景下,「蚂蚁动态卡片」孕育而生。
「蚂蚁动态卡片」现正式上线 mPaaS,欢迎广大开发者登录阿里云账号前往 mPaaS 控制台开通试用。
以下内容为「蚂蚁动态卡片」新品发布会全程回顾。
点击这里观看全程回放
蚂蚁动态卡片,实现App首页的敏捷更新
via 青葶-蚂蚁集团 mPaaS 产品经理
蚂蚁动态卡片是基于支付宝的全新卡片技术栈,实现 App 首页的敏捷更新。
蚂蚁动态卡片 Cube 是蚂蚁集团内部自研的一套跨平台、动态化的卡片解决方案,是服务于应用页面内的区域动态化技术,面向内容运营,帮助产品技术提升开发效率和运营效率。
每一个动态卡片独立嵌于原生页面内的一个区域,区域内容通过卡片模板进行展示。在支付宝的首页,疫情服务助手、蚂蚁森林或支付页面,都是通过卡片的方式实现的。
卡片具有以下几方面的优势:
① 跨平台的一致性:一套代码实现多端效果对齐。
② 动态化:卡片内可以进行页面结构样式以及业务逻辑的动态化更新。
③ 高性能:卡片的更新以卡片包的形式发布,它的包体积非常小,具有极致的性能和内存。
动态卡片是经过支付宝 App 业务深度打磨的技术栈,它包括客户端和云端,具有稳定性和可靠性(低于十万分之一的 crash 率)。同时,它有完善的开发调试工具,比如编译、预览、调试、发布等。
蚂蚁动态卡片的应用场景主要围绕内容运营,它可以实现新一代移动端动态研发的模式。伴随着富媒体内容井喷式增长以及内容的多样化、年轻化,为提升用户转化率、留存率,互联网以及传统行业都对移动应用的内容运营有着日益增加增强的诉求。如何提升内容的动态化运营效率,成为了各行业产研以及运营的重要课题。
而他们的诉求,都可以通过卡片来实现。
为什么移动端 App 需要动态卡片?
上图为蚂蚁动态卡片与原生 native 页面、 web 页面、 Flutter、 RN 的对比。在跨平台、开发效率、性能、包体积大小、接入改造成本、发布粒度等方面,动态卡片都具有较大的优势。尤其在发布粒度上,卡片可以实现在局部页面或某一个小程序或子应用层面上进行发布,且发布效率极高,发布即可见。同时卡片的包体积非常小,改造的接入成本也很低,并且具备多端的跨平台一致性。
Cube 经过长达四年的研发周期,通过类前端的开发语言,有着比较完善的研发体系。经过了支付宝钱包等大规模的应用,在首页、财富 Tab 和生活 Tab 都实现了良好的落地效果。
动态卡片对业务的价值在于能够帮助研发人员快速响应运营诉求,它的技术目标是兼顾用户体验和研发效率。
在客户端,卡片呈现的效果如上图右侧所示,它由两部分组成,分别是卡片模板和卡片数据。客户端通过获取卡片模板,再获取卡片数据,最终渲染出终端的样式。
卡片模版包含了卡片布局、卡片逻辑和卡片样式。其中逻辑包含埋点信息,以及比如在什么情况下卡片要展示何种样式等业务逻辑。
蚂蚁动态卡片的产品价值可以从三个维度来阐释:
① 面向开发:拥有高效开发的特性。通过精简的 VUE 语言、完善的调试工具等,减少发版,提升开发效率。
② 面向产品/运营:拥有实时更新的能力。每一次发版或有内容更新的时候,通过预置好的卡片模板将运营内容进行更新并推送即可。这也意味着大多数情况下,产品更新只需要发布一个卡片,而不需要进行整个 App 、整个小程序或整个界面的更新发布。从而使得运营效率和产品的需求落地效率很高,可以支持多个业务场景进行一个落地,也可以满足差异化和个性化的运营诉求。
③ 面向终端用户:提供了流畅体验。卡片可以媲美于原生体验,流畅度甚至优于原生。同时得益于包体积较小、性能好、占用内存少,面向重大用户时会有更流畅的体验。
蚂蚁动态卡片的产品架构分为四层:
① 技术层:通过 Cube 的内核衍生出了两个模块,分别是 Cube 卡片和未来将发布的轻量级小程序技术栈。
② 产品层:基于 Cube 卡片衍生出的产品有蚂蚁动态卡片 Cube 和蚂蚁动态卡片的智能版面;基于小程序衍生出的有 Cube 高性能小程序以及 Cube 高性能小程序-智能搭建。
③ 设备层:支持移动端和 IoT 端。在卡片层面上更广泛适用于移动端,而高性能小程序能够兼顾移动端的低端设备、 IoT 设备或大屏一体机等。
④ 场景层:适用于富媒体的数字化运营场景,包含金融、泛娱乐、互联网、交通、物联网等。
对于重视内容运营的行业,蚂蚁动态卡片能够提供高性能、高效率的内容运营,适用于千人千面的实时内容运营、舆情应急管理、运维应急管理,或产品的 AB 测试、最小化功能的更新等。需要紧急更新内容或动态化更新的场景下,不需要发布版本,只需更新卡片内容即可。
卡片支持的组件包括腰封、 feed 流、视频、浮窗、广告等原生支持的组件。相比于 Flutter 与 H5 无法支持腰封,且只能支持整个页面的发布,Cube提供了原生以及部分动态化能力, 能够支持的场景更为广泛。
场景1:运营场景下的动态卡片发布和智能版面千人千面。
上图左下角呈现了两个不同模板的动态卡片,它们的数据也不同。可以通过发布不同的卡片模板以及调用后端的不同数据,实现卡片的动态化更新以及千人千面的按需展示。整个首页即版面,可以针对不同人群设置不同的版面。
场景2:支付宝结合 mPaaS 新一代移动端动态研发。
上图右下角呈现的支付宝首页,其中蓝色部分是原生开发为主,体验最佳、性能最优;中间部分是内部一些动态的子应用,采用 cube 小程序开发,兼顾体验和动态性;最下方大盘晴雨表以及其他 Tab 详情页采用 cube 开发,具备局部动态化或单页动态化的高性能。
Cube 智能版面是基于动态卡片、智能引擎实现页面整体布局的千人千面。页面由若干个可以动态渲染的卡片模板组成,动态卡片的模板与业务数据进行对接,针对不同的人群定向推送。智能引擎可以通过版面的配置实现不同人群、不同内容的版面调整。
版面也可以与现有的一些 mPaaS 其他产品结合,比如短视频直播、终端智能、低代码搭建、 LBS 、可视化埋点等,实现整体解决方案的输出。
以支付宝首页动态化智能版面为例,可以看到活跃和不活跃两种人群的版面是有差异的。针对不活跃的人群,首页以优惠和公益为主;针对活跃用户,首页会显示其深度关联的业务,比如大盘晴雨表、理财热点等。此外,同样的用户,开市和闭市后理财卡片的样式和内容会不同,以及晚上会出现蚂蚁森林、步数兑换能量等卡片引导提示。
Cube 高性能小程序是基于大屏或 IoT 场景实现的新一代开发需求,它是基于 Cube 渲染引擎进行的页面级或子应用级的解决方案,是一种高性能、动态化的小程序解决方案,具备跨平台一致性、动态性、高性。同时在开发侧复用小程序 DSL 子集,上手更简单。
Cube 高性能小程序的适用场景多为大屏的 IOT 场景或一些低端设备的小程序,也适合 App 动态单页、常规 App 里的小程序。它在低端设备上的性能更佳,相比于外部的小程序,Cube 高性能小程序可以媲美原生的体验,因此在低端设备上更节省内存,且流畅度更好。
它的核心优势主要在于以下两个方面:
① 高效开发:复用小程序的开发语言,减少发版,提高效率。
② 流程体验:针对大屏 IoT 或机顶盒等比较老旧的设备,可以达到原生 native 的体验,也可以实现动态发布,流畅性也远高于传统的外部小程序。
上图为蚂蚁动态卡片与 H5 的效果对比。
左侧为冷启动、有缓存情况,卡片的启动速度更快速;右侧为滑动、有缓存情况下,卡片的渲染效果更流畅,速度也更快。
上图为蚂蚁动态卡片与原生的效果对比,整体差异并不大,卡片的性能可以媲美于原生。
动态,高效,蚂蚁动态卡片的内核逻辑
via 京君-蚂蚁集团高级技术专家
蚂蚁动态卡片封装了独立的 SDK ,并在 SDK 内部实现了卡片的渲染能力和 JS 相关的逻辑能力,应用可以非常简单方便地接入 SDK。同时为了提高卡片的渲染性,在内部实现了卡片间的复用以及卡片内组件的复用。
另外,动态卡片还实现了以下扩展能力:
① 扩展组件协议:卡片内部提供了内置组件,比如文本、图片、 div 等。通过对内置组件进行组合,能够实现大部分业务场景的诉求。但是有一些特殊场景希望能够扩展自己的组件能力。举个例子,比如业务在客户端已经实现了一些视频组件、直播组件定制化的能力,但依然希望这些组件能够在卡片上实现混渲染并达到动态下发的能力。提供了扩展组件协议后,只需要在现有组件上实现扩展的组件协议,用注册的自定义扩展组件标签来写模板,即可达到组件在模版内实现混合渲染的目的。
② 扩展 JSAPI:针对一些客户端有的能力,可以在卡片内通过 JSAPI 来调用。通过实现 JSAPI 协议,扩展 JSAPI,即可在卡片内部写 GS 逻辑的时候直接调用端上的公共能力。
应用接入动态卡片后,主要工作是创建卡片来实现内容的动态化。SDK 提供了创建卡片的接口,创建的卡片即为 card 实例。页面里的一个 view 对应一个 card。可以通过 view 实现内容和逻辑,并生成 card,通过 card 实现内容的动态化。每一个 card 都有卡片模板,由统一的 DSL 负责写卡片模板的布局、样式以及卡片内的 JS 逻辑。DSL 内置的组件标签以及扩展组件都可以在模板里使用。
卡片的系统架构分为以下四层:
- 应用层:主要负责数据加工处理、卡片模板的版本管理以及对外负责 API 的协议封装。
其中模板管理主要根据不同的客户端版本下发不同的模板,根据高版本的模板实现动态升级,保证客户端的卡片能实时更新,达到动态渲染的目的。可以用不同的版本管理卡片的样式和布局,而在模板内部,因为支持 JS 逻辑的编写,也可以支持逻辑渲染的能力。在单个模板内部也可以根据不同的服务端下发数据条件来实现不同的样式渲染,但是这会导致模板的业务逻辑特别多,难以维护。
因此,通过不同的模板的版本来管理更方便,也比较适合大部分动态化场景。
- 逻辑层:包括 JS 框架和模板引擎,通过模板引擎实现模板样式的解析、属性数据的绑定以及逻辑渲染、指令构建。指令构建是指根据模板的布局、样式属性,构建出不同节点的指令来分发到渲染层做节点渲染。此外,逻辑层还实现了比较小的 JS 框架,用于辅助模板的动态计算能力。
- 渲染层:主要负责渲染模型的计算,包括布局、分层计算、渲染任务的构建以及光栅化。渲染层包含了两类不同的 UI,分别是实体 UI 和虚拟 UI。实体 UI 指一些复杂组件,比如输入框、列表以及自定义的扩展组件;虚拟 UI 指通过 Canvas CPI 绘制的组件,包括内置的文本组件、图片组件、DIY 的容器组件等。
- 平台层:封装了 Canvas CPI 和平台 UI 接口。实体 UI 的布局通过平台层的接口来实现。
在 JSFramework 层实现了很小的 JS 显示式框架,设计初衷是为了实现高性能,因此只是实现了响应式的能力,而没有过多扩充 JS 框架的能力。我们选择了 QuickJS 引擎作为卡片的 JS 引擎,它的体积比较小,而且性能比较高,非常适合动态卡片场景。此外,这一层也扩展了 JSApi 和 Timer 异步任务的能力。
Card Engine 层主要包括了卡片的 Module 管理、组件管理、简单模板的表达式、抽象语法树的解析以及 DOM 的 diff计算。
RenderEngine 层是整个渲染的核心的模块,包括布局、动画能力(2D、3D)、Render、Layer、手势和其他通用事件。Render 主要负责计算一些渲染相关的数据,Layer 主要负责分层的计算。
动态卡片的高效得益于线程模型的设计。运行时的常驻线程主要有以下几个:
① Bridge 线程:负责 Js 指令、Node 树构建、以及相关节点的样式解析和布局的计算。
② Render 线程:负责 Render 树和 Layer 树的构建以及分层绘制树、手势相关的数据计算和渲染数据的计算。其中 Render 即渲染的起始数,Layer 即分层绘制数。
③ Paint 线程:负责绘制指令的构建以及最终的光栅化。Paint 线程是多线程,能够支持多任务做渲染绘制,提高渲染性能。
④ 主线程:主要包括手势识别以及 UI 管理。
除了以上四个主要线程,在初始化阶段,还有 worker 线程以及 IO 相关的线程。
数据模型主要包含以下四棵树:
① NodeTree:原始的节点树,布局的计算以及数据的变更都依赖于它。
② RenderTree:渲染的变形的数。它的层级可能会发生变化,比如模板里 zIndex 涉及到一些层级变化,做 render 树计算后最终的层级会与实际的 zIndex 能力一致,因此 render 树它会产生节点层级的调整。
③ LayerTree:它主要的作用是分层处理。不同的节点组件可能在同一层进行渲染绘制,而每一个 Layer 节点都是独立的渲染容器,其中包含了很多虚拟节点,都在同一层里进行渲染。因此每一个 Layer 节点之间都是独立的,可以实现并发渲染。
④ PaintTree:在每一个 Layer 的节点内部会有虚拟节点树,它们在一层内渲染,但同时也有自己的层级关系。
从最开始的原始数据,经过加工,到 NodeTree、RenderTree,最终到 LayerTree,最终是以 LayerTree 为渲染容器。每一个 Layer 节点对应一个独立的渲染任务,它们相互之间没有任何关系,可以实现并发渲染。下面那个就是一卡片的,它整个在初始化到最终上屏的这么过程。
上图下方的 AB 两张卡片的上屏全过程。首先从 UI 线程触发上屏,在 worker 线程里进行初始化,完成后进入 Render 进行分层计算、手势绘制任务计算,再到 paint 线程进行多线程并发渲染,最终回到 UI 上屏。
上图可以看到 A 卡片的内部实现了多线程的渲染,卡片之间是相互独立的,能够支持异步渲染,且能够保证较低的白屏概率。
卡片的生产/工作流程如下:研发期主要负责卡片的编译、调试和预览。卡片完成编译调试后,再通过卡片关联后台完成卡片的上传和发布。发布完成之后,在端上通过 CardSDKName 获取到最新发布的卡片,即可在设备上实现动态化渲染卡片内容。根据不同的卡片模板可以渲染不同的卡片样式、布局。
ACK 工具提供了卡片的调试、编译功能。通过 ACK 工具可以快速迁移新工程,并编译调试代码,最终通过 playground 预览。客户端 playground 集成 到debug 包里,能够通过 ACK 工具在本地做编译并预览。DSL 使用了 VUE 的子集语法,可以用前端的任何 ID 来编写,最终使用 ACK 工具做编译、预览即可。
接下来为卡片从新建到最终预览的全流程演示。
首先,通过 ack-h 查看其中涉及的命令。
其中有卡片初始化的命令、简单的脚手架、工具名以及预览。
接下来,通过 act init 命令初始化新建卡片工程。
上图为基础的卡片信息包,包含了卡片的路径、名字等信息。
用 VSCode 将卡片打开。
上图左侧即卡片的工程文件,包括工程配置文件、main.vue(卡片样式、逻辑文件) 、 manifest.json(卡片的配置文件、模版本号、卡片名称)、mock.json(本地 mock 文件)。
打开 main.vue,其中 template 字段包含了卡片主要的节点样式、布局等,script 包含了 JS 的变量声明、生命周期、JS 逻辑等。methods 里面可以定义 JS 方法,template 里绑定的 click 事件内定义了 onClick 方法,可以在其中写一些自己的逻辑。
style 内可以自定义节点的样式。
新建完模板后,需要通过“act prepare && act server”指令生成二维码,用以与调试工具的连接。
只需要用 debug 调试工具扫描二维码,客户端就能够与工具的本地模板建立连接了,即可进行调试和预览。
使用“act build ”命令编译本地模板。编译完成后,左侧新增了 dist 目录,它是模板的编译产物。
输入“act preview”预览指令,设备显示如下图:
对代码进行如下修改。
编译,预览后,设备显示如下:
ACT 提供了 alive 指令,修改模板之后只需保存,不需要手动编译、预览,即可查看最终效果。
接下来为相对复杂的模板演示。
在新建功能后,需要重新扫码建立连接。通过 build 指令预览,效果如下:
在代码内加入 key frame 动画,预览查看以后可以看到效果非常流畅。
蚂蚁动态卡片已经落地于支付宝钱包的诸多场景,有了很多积累和沉淀,实践也证明了它的性能和稳定性都能够经受住考验,希望它能被更多开发者看见并使用。
END