🙋🏻♀️ 编者按:Cube 起源于 native 页面的动态化诉求,产品形态表现于 Cube 卡片。随着小程序概念的出现,Cube 融入了支付宝小程序技术栈,产品形态为轻量级的支付宝小程序解决方案。本文为《Cube 技术解读》系列首篇文章,作者是蚂蚁集团客户端工程师入弦,欢迎查阅~
背景
支付宝客户端的动态化技术经历三个阶段。第一个阶段是 native+web 的 hybrid 模式,以 webview 为基石。第二阶段是实体组件模式,把 html 描述的组件和 css 样式信息映射到实体组件,并且把实体组件的事件传递到js层进行处理。第三阶段是实体组件+部分光栅化的hybrid模式,Cube 是第三阶段的产物。Cube 起源于 native 页面的动态化诉求,产品形态表现于 Cube 卡片。随着小程序概念的出现,Cube 融入了支付宝小程序技术栈,产品形态为轻量级的支付宝小程序解决方案(相对于使用浏览作为核心的 web 小程序)。这篇文章是一个综述,也是 Cube 系列的首篇文章。
技术选型&演进
Cube 的准确诞生时间很难确定,大致在 16 和 17 年之间,比 RN(ReactNative)晚上一年。Cube 诞生的主要原因是 native 页面的动态化诉求。钱包改版的频率高,给研发的压力很大,于是想到把高频改版的页面动态化。RN 和 Flutter 的出现,给了我们一个很好的观察视角,即业界优秀的科技公司是如何看待动态化这个话题以及它们的答案。起步阶段,我们达成以下共识:
1、独立研发,自主可控。我们没有选择基于 RN 的开源代码来实现我们的动态化解决方案,也没有 Flutter 公布源码后,切换到 Flutter。这么做是考虑到两点,第一点,技术栈的演进要掌握在自己手里,不希望被牵着鼻子走;第二点,开源项目的产品化成本并不低,后期的维护成本也不低;
2、服务业务,技术克制。首先,我们没有足够能力和资源来支撑一个通用技术产品,服务于钱包业务是第一位的,简单说就是贴着业务走。其次,我们拒绝只求花里胡哨的技术 demo,把核心能力做好,把产品成熟度做好,考虑拿到业务价值是第一位的。
基于上面两个共识,我们的技术选型如下:
- 选择 Javascript 作为逻辑语言;
- 选择 CSS 的某个子集作为界面描述语言;
- 自绘制(text/img/div/scroller)+ 原生组件 (input, animation,map, audio, video …)的混合渲染模式。
蚂蚁在前端的积累比较多,Cube 选择拥抱前端,采用 javascript 和 css 是自然的事情。默认 js 引擎是 quickjs。没有选择 v8,有两个判断:v8 太重,内存开销和库加载速度都不理想;Cube 的应用场景大概率不需要 v8 提供的 jit 能力。我们额外引入了第三方的 wamr ( https://github.com/bytecodealliance/wasm-micro-runtime ) 作为 webassemby 引擎,且在编译构建工具上支持 javacript 和 assemblyscript 混合开发。Flutter 开源后受到很多人的追捧,在很多文章和 ppt 上都看到了“Flutter 完全独立于平台层的渲染管线的优势”表述,认为比 RN 映射实体组件的方式要高级很多。我们不认为 Flutter 的渲染管线的性能优于操作系统的渲染管线,毕竟设备和操作系统可以垂直整合,利用一些设备特性。此外,是否自建渲染管线应该取决于业务诉求,而不应该盲目的追求技术。
Cube 的自建渲染管线仅限于自绘制标签,如前所述包括 text/img/div/scroller,使用平台层的 canvas api 直接绘制在系统的 view 上;如果某颗子树的标签都是自绘制标签,这颗子树会被“拍平”绘制在一个 view 上。自绘制标签以外的标签都是用映射原生组件的方式,并且封装了统一的实体组件映射些协议,提供给开发人员。目前 Cube 的业务场景主要集中在移动端,也简单尝试过往 linux/rtos 平台移植。如果后续业务逐渐扩展到 linux/rtos,我们会考虑进一步完善自绘制,一个是把平台层的 canvas api 收敛到 skia,另一个是内置 layer compositor。
当前状态
在承接业务的过程中,Cube 大致沉淀了 2 种业务形态,分别是 Cube 卡片和 Cube 小程序。
Cube 卡片的作用是给 native 页面赋予区域化的动态能力,提高业务迭代和运营效率。钱包接入的卡片也分为两类,一类是没有 js 能力的简单卡片,支持表达式和 vif&vshow 这类构建时控制 DOM 树的操作,追求近似 native 的速度;另一类是具备 js 能力的复杂卡片,用来支持一些复杂的业务。Cube 卡片在钱包已经大规模应用,pv 超过 100 亿,接入的场景参考截图,包括不限于首页、理财、我的等 tab 页,以及卡包、出行、支付结果页等二级页面。
Cube 卡片的定位也是优先服务于钱包内的一二方业务,如果要想提供给三方开发者区域动态化的能力,我们推荐小程序 widget。此外,我们正在着手把 Cube 卡片能力输出给中小型金融机构以及互联网公司。
Cube 是作为渲染引擎来引入小程序技术栈。小程序基础设施包括:容器,前端框架,渲染引擎,脚本引擎。容器可以理解成 Appx/渲染引擎/脚本引擎之间的聚合层代码,提供包管理/JSAPI/安全管控/钱包核心服务等功能。移动端上小程序默认的渲染引擎是 UC,Cube 小程序应用很有限。相对于 UC 来说,Cube 在包大小/启动速度/列表滑动流畅性/内存消耗上有一些优势,但是劣势也非常明显——Cube 支持的 css 能力不足,且 Cube 的开发工具不完善。基于此,从 19 年开始 Cube 投入了巨大的人力来扩充 css 能力。Cube 是除浏览器内核外支持 CSS 较完善的渲染引擎,支持 flex/inline/block 等布局方式,伪类和伪元素,z-index 以及相对和绝对定位层级管理。我们也投入大量的精力试图建立类似 devtools 功能的工具。
这些努力一定程度上改进了开发效能,但仍然无法满足前端同学的诉求。我们逐渐意识到,在浏览器性能不是主要瓶颈的场景下,前端开发者不大会接受浏览器的一个子集。于是,Cube 小程序开始转向 IoT 场景,面向浏览器跑不起来,或者,体验极差的场景。Cube 小程序作为某种应用开发栈,对试图建立三方开发者生态的客户是有一定的吸引力。目前我们主要的精力在电视大屏端,感兴趣的同学可以在天猫魔盒上体验 Cube 小程序,也可以在别的盒子以及智能电视上下载酷喵影视(https://acz.youku.com/wow/tvact/act/cibn?spm=a2hpd.20022520.sort.1!2~3~P~A )。
在卡片和小程序之间,实际上还有一个中间地带,即单页。这个页面可以是全屏,也可以是漂浮在空中的半屏。Cube 早期尝试过 h5 单页,面向高频率营销场景。它的技术栈和小程序几乎完全一样,不同的是,h5 单页没有容器的概念,从服务端下载到端上的不是小程序包而是嵌入了 Cube 构建产物的 h5 页面。h5 单页接入过红包码业务和蚂蚁森林的二级页面,因为维护成本陆续下线。h5 单页不成功,并不意味着单页的需求不存在。近期探索的小程序 widget 其实就属于单页的范畴——我们希望 widget 能够让服务前置,承载一定的交互逻辑,同时也限制它的能力,便于管控,适合三方开发者。
技术架构
Cube 的内部有两个大的模块,一个是 CubeKit,负责对接 js 引擎且封装平台差异,也包括了开发调试工具。另一个是 CubeCore,是用 c++ 代码实现的渲染核心逻辑。
对于 Cube 小程序,支持 tinyApp-dsl 子集,移动端上使用 jscore/v8 作为 js 代码的执行引擎,IoT 设备上使用 quickjs;对于 Cube 卡片,支持基于精简 vue 的 card-dsl。简单的卡片直接解析 AST 来渲染页面,复杂卡片支持用户用 js 写一些简单逻辑,并且通过 quckjs 来驱动 dom 树的更新。
移动端上,Cube 和 Web 小程序共用一个容器代码。在 IoT 设备上,我们持续投入人力到 Appx 和容器的垂直整合中。从目前的数据来看,IoT 上的 Cube 小程序相对移动端的 Cube 小程序有不小的基础性能优势。在电视端上 Cube 小程序的基础性能数据是:包体积 5.5 mb,内存消耗 32 mb(淘宝特价板小程序为例),冷启动耗时3~4s。随着垂直整合的深入,未来 Cube 小程序的基础性能会进一步的改善。
质量体系这个话题,我放在技术架构里讲,原因是它本身是技术架构的一部分。做业务开发,测试人员可以遍历用户场景,有 bug 修 bug。基础软件所承载的业务场景只是无限样本中很小的一部分,业务场景的回归没有问题,不能够保证引擎没有问题——最坏的情况是问题持续累积,直到某一天突然爆发出来。这个时候再想解决问题,已经积重难返。所以,基础软件的研发迫切需要某种提前暴露潜在问题的手段,这个手段不可能借助某个测试资源而是研发团队自己建设。
浏览器的 WPT 测试用例集合给了一个很好的参考,Cube 也建设了这样一套基础能力样本集合以及配套的样本自动化执行框架 KITE,投入到版本迭代&代码提交中。截止目前,我们基本能做到单日粒度的自动巡检,支撑我们在已有大量的业务场景的情况下对引擎做升级和重构,下图是引擎基础能力巡检工具的截图。
开发工具链这个话题,我也放在这里讲。Cube 的直接客户不是用户,而是业务方的开发同学。在项目初期就要考虑到工具这块,比如调试器的设计、预览容器、日志设计、低代码搭建平台等等。在扩展业务过程中,工具链某种程度上比 Cube 本身还要重要,毕竟它是客户的第一印象。我们遇到过前期技术调研时,客户因为工具的不完善而拒绝使用。业务接入后,除了能力上,业务方也会对工具提供各种要求(在协助排查问题时也会发现新的工具需求),贯穿产品的整个生命周期,也是维系客户粘性的重要工作。随着 Cube 大规模应用于业务后,我们在工具上投入的精力逐渐超过了功能&技术迭代本身。
回顾&未来规划
回顾过去 5 年,Cube 一路跌跌撞撞,中途差点夭折,能走到今天实属不易。从个人视角,Cube 能活下来依赖“上下坚持”。一方面,上面的决策者坚持投入(19 年及以前几乎没有像样的业务价值);另一方面,一线的同学坚持做一件事,没有技术追求是不可能挺过途中的各种坎坷。我们期待能 Cube 未来应用到物联网操作系统,毕竟应用开发技术栈是操作系统的核心技术之一。
Cube 未来的规划继续坚持“紧贴业务”和“技术克制”,把产品做好,把开发者服务好,把技术打磨好。重点的发展方向如下:
- 鉴于 Cube 卡片可以运行在 32MB 内存/400Mhz的 RTOS 设备上,进一步探索在物联网设备上的落地;
- 推广 Cube 小程序在电视大屏端的应用和落地,探索商业模式。
如前所述,后续更新文章我会更侧重技术详解,针对卡片技术栈、小程序技术栈、质量 KITE&工具 ACT、性能优化等进行深入解读与畅聊。如你对该系列文章感兴趣,亦或是对 Cube 感兴趣,你也可以在后台进行留言。