本文为《Cube 技术解读》系列第七篇文章,往期文章欢迎大家回顾:
一直以来移动端技术生态中的动态化技术都是行业内的话题热点,各类应用框架也层出不穷(RN、Weex、Flutter),支付宝客户端也在业务旺盛的高性能、易投放诉求中逐步沉淀下了一套自研解决方案--Cube 卡片。
在移动端动态化应用场景中,怎样才能解放 DSL 领域内描述语言的高效生产力?怎样才能有效的降低传统移动端 Native 研发的调试成本?怎样才能贴切的整合行业内各类优秀解决方案与工具?
带着对这些问题的思考,支付宝 Cube 团队在卡片业务中摸索沉淀了连接开发 DSL 与引擎运行时的基础配套工具--Ant Cube Tool(简称 ACT)。这篇文章将尝试带着大家了解 Cube 卡片工具 ACT 的设计思路与演进历程。
技术选型
CLI
Cube 卡片的动态化方案主要是基于 JS 桥接技术,DSL 采用的是 HTML + JS + CSS 近前端技术栈的格式,行文写法上借鉴了 Vue 单模板格式。行业中前端生态内工具广泛采用的是基于 Node 环境的 CLI 形式,考虑到这种 CLI 形式轻便、灵活、易于维护,Node 生态丰富的社区资源,再加上 Node 天然的跨平台特性,Cube 卡片的基础生产工具也决定使用 Node CLI 技术栈进行建设。
IDE vs CLI
随着 Cube 卡片在业务场景中的逐步开放,越来越多拥有不同技术背景(前端、Native)的开发同学开始使用 Cube 卡片工具进行迭代研发。虽然 CLI 可以满足基本的日常开发调试诉求,但相较于传统 IDE 模式,可交互的手段(快捷键、按钮、菜单等)以及信息回馈的形式(弹窗提醒、编辑器联动、状态通知等)就会变得力不从心;然而搭建一个功能完备、灵活可靠、开箱即用的卡片桌面 IDE,不可避免的需要投入大量的研发、维护成本。得益于行业内流行的编码工具 VSCode 提供了较为完善、丰富的插件扩展能力,我们将 VSCode 插件作为 CLI 的辅助工具,补充了基于 VSCode 环境的便捷交互方式,在 IDE 与 CLI、研发投入与体验产出之间寻求到了平衡。
框架设计
ACT 作为 Cube 卡片最基础的辅助工具,承载着卡片在生产阶段最核心的研发诉求:编译、预览、调试,操作链路上需要连通源码编辑器、构建底层、设备运行时各个环节,我们采用的是基于 CS 模式的数据通信机制,核心能力将封装在 ACT 本地服务器中,由服务器提供 REST 响应接口,供桌面编码环境、设备运行环境、Web 调试环境调用。同时本地服务器中也开放了 WebSocket 连接能力,在链路节点间提供消息推送的功能,保障节点间状态同步的及时性。
有别于传统 Native 单台设备的联调体验,在 CS 模式下,ACT 可以很方便的实现多台不同平台、不同配置的设备同时进行卡片预览、调试,这对在开发阶段及时验证卡片效果跨平台的一致性将很有帮助。值得一提的是,在同一局域网环境下,Client 与 Server 的连接访问其实并没有地理位置的限制,这也使得我们在开发卡片时可以将本地的效果、信息投递给远程协作的其他同事,提高协作研发的效率;同样的,也可以方便的在云端设备平台环境中挑选合适的固件设备进行远程连接,降低了不同运行环境兼容性验证的操作成本。
核心功能
ACT 最主要的职能就是将业务开发时提供的模板信息(卡片 DSL)转换为运行时可被处理的 Dom 数据元、Css 样式集以及 JS 逻辑段,这个过程我们一般称之为编译。在获得编译产物之后,ACT 会继续向引擎运行时投递产物结果,并借助内置于运行时的 Playground 模块,为业务卡片提供真机效果预览能力。同时,通过 ACT 内置的 Devtool 应用,为正在预览的卡片附加 Element 节点、Style 样式以及 JS 断点调试能力。
工程
卡片的源码工程主要由工程配置、卡片配置、卡片模板三部分构成。其中工程配置(.act.config.json 文件)主要声明了预览模式、源码路径等基本信息;卡片配置(manifest.json 文件)包含了编译模式、产物类型、引擎配置、组件声明等信息;卡片模板(main.vue 文件)则涵盖了一个卡片最基础的布局、样式、逻辑等部分。
➜ Card tree -a . ├── .act.config.json ├── main.vue └── manifest.json
编译
在卡片的 DSL 描述格式中,一个基本的卡片主要包含布局结构(<template>
)、样式声明(<style>
)、逻辑处理(<script>
)三个部分。ACT 的编译过程,就是把 DSL 中的这些基础信息,转译为在运行时可被卡片引擎消费的元数据。虽然在行文时卡片的 DSL 参考了 Vue 格式,但卡片引擎的运行时 JS 框架同 Vuejs 框架完全不同,所以,ACT 对.vue 文件的编译处理也同 VueCLI 完全不同。在编译过程中我们引入了 CardAST 的结构,编码时提供的 DSL 源码可以在编译后经过 CardAST 中间态结构格式化为运行时可以处理的二进制格式;同时,通过 ES import 语法、模板中 Style 段的 src 属性,支持了 JS 逻辑段、CSS 样式段按需组织的能力。
预览
在完成卡片 DSL 编码后,需要通过真机运行验证卡片的效果是否符合预期,ACT 提供了基于 CS 模式的真机预览能力。在桌面本机环境中 ACT 在 KOA 框架下实现了一个本地 Server,并开放了 REST 请求 API 以及 WebSocket 通信连接能力。编译产物将在桌面本机环境中被压缩打包,并通过局域网内来自客户端的下载请求,将卡片产物投递至客户端运行时环境,借助客户端内置的 Playground 模块,可以模拟卡片产物的下载、解包流程,并提供适当的容器页面加载卡片,卡片的渲染效果以及相关的事件交互都可以在 Playground 模块中进行模拟验证。
为了简化操作流程,ACT 也整合了编译、预览两个独立的子命令,在 Node 环境文件系统变更通知的事件下,提供了 Alive 实时预览能力,帮助开发人员可以更高效的编写、验证卡片效果。
调试
另一项同卡片研发阶段密不可分的能力就是卡片调试功能。作为一个更多的服务于客户端 Native 动态化场景的技术栈,卡片的调试能力与传统的 Native 开发 IDE 调试方式不同,更多的借鉴了前端应用的调试方案。
ACT 提供的卡片调试能力主要包含两个方面:模板节点调试、JS 断点调试。
模板节点调试在前端行业内较为流行的 CDP(ChromeDevtoolsProtocol)协议框架下,将源自客户端运行时按照 CDP 协议格式 dump 出的消息数据,通过 ACT 内置的 Devtools Frontend 前端应用提供信息展示与操作交互能力,可以实现卡片模板节点相关的 Element(元素节点)、Style(样式节点)查看、编辑等调试效果。同时,遵照 CDP Overlay、Log 协议,ACT 也实现了划屏高亮、实施日志等功能。
JS 断点调试在技术选型时则主要考虑的是希望能通过一个方案,同时覆盖不同 JS 引擎(V8、JSC、QJS 等)场景。ACT 将 JS 断点调试实现策略的重心集中在对卡片运行时 JS 框架交互协议的适配上,同时借助 NodeVM 环境与 VSCode 编辑器对 JS 断点调试的内置支撑能力,将客户端 Cube 卡片引擎的 JS 运行时完整桥接至 NodeVM 环境中,通过拦截卡片 JS 框架的运行时交互指令,将响应逻辑中转至 NodeVM,完成相应的单步调试后,再将待处理的 JS 指令流转回 Native 环境进行消费,从而模拟卡片 JS 逻辑链路中的断点调试效果。
功能演进
ACT 目前已经迭代至 3.x 版本,工具链的整个功能演进思路可以用三个问题来说明:
- 如何满足卡片研发最基本、最核心的能力诉求?
- 如何编写更可靠的卡片模板?
- 如何更便捷的编写卡片模板?
围绕三个问题的解决思路也贯穿了 ACT 工具链的整个迭代周期,对应的,在卡片的运行期、编译期、编码期分别落地了适用的三类解决方案。
运行期
如何让卡片模板能在客户端运行时顺利的“跑”起来,这是 ACT 工具链要满足的最基本、最核心的诉求。上述核心功能章节提及的编译、预览、调试功能基本都是围绕这一主题而落地的具体能力,这也是早期 ACT1.x 版本阶段的主要迭代内容。ACT 1.x 版本是整个工具链的起点,也为 Cube 卡片技术在支付宝业务场景中的快速应用提供了有力保障。
编译期
Cube 卡片技术源自对 Native 动态化能力的探索,Cube 引擎本身也主要由 Native 端侧技术实现,无论是功能迭代、还是问题修复,不可避免的都会遇到一般客户端 SDK 需要面对的版本兼容、历史缺陷等问题。如何保障卡片在应用时的稳定、可靠性成为了 ACT 2.x 版本的主要课题。
ACT 2.x 版本在卡片源码的编译期,引入了 Lint 静态语法检查的能力。整体 Lint 框架借鉴了行业内较为成熟的 ESLint 方案,并根据卡片 DSL 的特点,针对 template 布局描述、css 样式描述、json 配置描述扩展了 Lint 框架,将仅适用于 JS 语法检查的 ESLint,扩展为一个可以同时应对 template、css、json、js 所有格式的更为完善的 CardLint 方案。
CardLint 通过对卡片 DSL 相关源码的解析,提取 AST 结构,并基于卡片特有的元数据内容,有针对性的提供 template 节点属性、css 样式属性、jsapi、配置字段等校验规则(Rule);同时,还会扩展出卡片三方注入元数据、元数据版本兼容性等在实际业务场景中更有价值的校验规则。通过内置的 200+AST 校验规则,在卡片的编译阶段及时将异常信息反馈给开发人员,以达到“少踩坑”的目标,帮助卡片业务构建更可靠的模板。
17:55:23 ACT INFO 执行 LINT 检查 17:55:23 ACT WARN LINT 错误 [19,17] /test/card/tpl/main.vue 函数 selectorQuery 在版本 [*, 10.2.52] 存在问题: JSAPI dom 模块 selectorQuery 方法查询节点信息,当节点不存在时,闪退,请调整卡片 card 相应的处理逻辑 17:55:23 ACT WARN LINT 警告 [20,23] /test/card/tpl/main.vue 函数 loadKeyframes 在卡片 card 将投递的运行时端版本 \* 存在已知问题: JSAPI animation 模块多次调用 loadKeyframe 会导致闪退,请确认是否符合预期。 17:55:23 ACT WARN LINT 错误 [33,6] /test/card/tpl/main.vue 卡片仅支持单选择器声明,不允许进行选择器分组或组合 17:55:23 ACT WARN LINT 错误 [33,11] /test/card/tpl/main.vue 卡片不支持伪类选择器 17:55:23 ACT WARN LINT 错误 [37,10] /test/card/tpl/main.vue 不支持 !important 17:55:23 ACT ERROR 编译失败 ACTLintError: ACT Lint 检测到 4 个错误,1 个警告
编码期
在解决了卡片研发基本“温饱”问题后,ACT 3.x 版本的迭代重心集中在了如何帮助开发人员更高效的编写卡片源码上。VSCode 作为当前流行的代码编辑器,其丰富的 API 定制能力为 ACT 在增强交互手段上提供了非常大的帮助。而 VSCode 提供的 LSP 接入能力,也为 ACT 3.x 的解决方案打开了思路。
ACT 在 CardLint 的基础上,整合了卡片元数据相关的 CardMeta 模块,借助 VSCode 插件 API,提供了一个内置的 ACT VSCode 插件,将 Lint 提示、元数据使用文档以 VSCode 插件异常提示、浮窗文档、代码补全等形式重新进行了交互封装,落地了 CardCode 辅助模块,帮助卡片开发人员在代码编辑器中就可以及时的参照示例、注意事项等信息更便捷的完成卡片源码的开发工作。