DX全称DinamicX,目前是在淘宝乃至整个阿里集团内广泛使用的Native动态化方案,核心优势是性能和稳定性。过去几年一直有其他淘宝/集团的外部文章中有涉及到DX,但DX一直没有对外做过完整介绍,对外界来说这两个字母颇有些神秘色彩。本系列文章《DX研发模式》我们就将拉下它神秘的面纱,看看过去两年 DX 在做什么。
本文主要阐述 DX 在追求极致的性能体验过程中,所突破的性能瓶颈与实践经验。
背景
随着应用DinamicX(简称DX,下同)技术的场景和团队愈加复杂与广泛,持续保障DX核心竞争力,支持团队级别协同开发,助力复杂业务场景的诉求愈发强烈。之前的DX开发基于模板平台,其核心为基于开源的Monaco编辑器(驱动VScode的开源代码编辑器)定制开发的前端工程。虽然模板平台在过去一定程度上满足了业务开发需要,但其编辑体验,调试体验和性能保障等愈发难以满足开发者要求。
面对上述这些问题,从DX业务研发的需求角度看,我们需要一站式的开发环境,提供从创建,开发,编译,调试,到发布的全研发周期保障。
全研发周期保障
技术方案选型与设计
基于上述考虑,我们需要的是一个IDE。不同于模板平台这样的Editor,IDE提供了完整的开发周期支持。目前业界主流的VSCode,不仅提供了丰富的API用于做扩展,也包含了极其丰富的插件市场,可通过开发VSCode插件的方式来提供DX IDE。
VSCode生态
- 模板平台运行于Web环境,受限于浏览器, IDE运行于本地环境,扩展丰富容易,环境可控兼容性好;
- 编辑器可用来做丰富的编辑期支持,其LSP API除部分API(调用栈,快速修复,展示所有引用,动态计算表达式,内联值计算,类继承关系,符号表)不支持外,提供了丰富的编辑补全文档等功能;
- 相较于纯粹的编辑器,IDE则提供了完整的开发周期支持,包括项目系统,代码历史上下文理解,调试,同其他功能的结合(如脚手架,编译等),应用研发周期管理,平台相关的内容(如Swift, Kotlin, Faas)等。
虽然VSCode提供了诸多能力及扩展,包括但不限于文件树及菜单,工程体系,编辑器与语言服务(智能感知),调试服务,更好的集成构建与开发生命周期管理,平台工具相关等,但在面向开发者的交互层面,仍存在诸多限制与不便。与此同时,发源于阿里经济体内部的IDE框架--OpenSumi框架 (阿里&蚂蚁自研 IDE 研发框架,已开源),既可兼容 100% VS Code API,也扩展了诸多API,提供了丰富的UI自定义支持,大大简化了自定义插件的交互开发,因而我们采用OpenSumi作为IDE最终的技术选型。
OpenSumi定制扩展
如上,下图简要说明了OpenSumi对于自定义视图的支持。
结合上述的开发目标和技术选型,我们有了如下的设计。
设计大图
分层设计
总体来看IDE分为以下几个层次:
IDE容器(CLI)
如上我们使用了基于OpenSumi框架的IDE基座,其不仅兼容了VSCode生态,还提供诸多API等扩展,形成了ide-framework。再通过针对经济体场景下的定制,如登录态,前端开发工作台等,打包构建了O2这一最终的交付产品。对于我们而言,就是通过开发兼容VSCode API的OpenSumi插件,通过在O2上安装插件以达到最终的产品交付。
考虑到命令行调用、代码复用的原因,我们设计了基于Node.js的CLI用于支持诸多基本功能,如创建模板,编译模板,发布模板等。这里也有一个两级分层。即提供通用的调试服务,静态文件服务的utbd-devtools,以及基于utbd-devtools的各个不同CLI/IDE插件(通过DaemonInterface同utbd-devtools进行交互)。
底层能力
这里主要抽象了一些同模板开发没有直接关系的一些公共服务,包括调试协议(通道)、数据的存储与持久化、单例对象的管理池、Git服务、数据埋点服务等。
基础服务(模板开发)
这里主要是模板开发所需要的一些基础服务。
从编辑的角度而言,主要是自定义的DSL下的代码智能感知,核心是要一方面去实时分析源代码的语法树结构,一方面要结合DSL本身的约束与规范来判断各个树节点(及属性)是否合法等,再结合VSCode本身提供的语言服务器协议,实现最终的代码智能感知。从DX的角度而言,我们需要考虑以下DSL的分析:
- xml:即main.xml,其描述了视图的结构与样式,对其分析有成熟方案;
- json:包括event_chain.json和mock.json, 用于描述事件链和mock数据,对其分析有成熟方案;
- 表达式:基于模板设置的数据源(data),开发者可通过表达式的形式来描述属性,如:
<ImageView width="match_content" height="14" scaleType="fitXY" imageUrl="@{data.picUrl}" visibility="@{data.picUrl?'visible':'gone'}" />
此处的imageUrl和visibility属性即通过表达式书写,表达式不仅可用于XML,也可用于事件链文件。表达式语法是一个删减版的TypeScript语法,通过书写antlr4语法文件(Lexer.g4和Parser.g4),使用antlr4ts工具即可自动生成对应的Lexer.ts,Parser.ts,ParserListener.ts和ParserVisitor.ts文件,从而用于后续AST分析。
分析获得DSL的AST后,通过元数据(平台约束的控件、表达式、原子能力等)的约束,结合VSCode LSP下支持的代码智能感知API,我们即可实现包括补全、悬浮文档、诊断、跳转定义、代码格式化、折叠、高亮等在内的代码开发支持。
Feature开发与研发模式组装
我们按照研发模式包括一条工作流,工作流又可以分为多个阶段(准备阶段、创建阶段、编辑阶段、调试阶段、测试阶段、发布阶段),每个阶段又包括多个Feature(如创建阶段包括创建工程与创建模板)这样的层次关系,通过开发Feature最终组装成DevelopPattern。
研发模式扩展
实践中,不仅存在各类研发模式等的诉求,因为各种原因,还存在不少基于研发模式的自定义扩展,如消息业务域有基于DX模式的脚本扩展,菜鸟和CBU有基于DX模式的容器搭建扩展。为此,我们还设计了研发模式扩展插件,容许插件对上述的研发模式进行自定义扩展以满足各自诉求。
IDE功能与使用
本文只介绍DX研发模式相关功能。
功能区图示
创建阶段
创建阶段包括DX工程与模板。
创建工程
- 设置GitlabToken(仅一次,以便自动创建Gitlab项目)
- 点击创建工程入口
- 选择研发模式(此处应为DX)
- 配置参数,创建项目
创建模板
【新建模板】
点击创建模板入口
配置参数,创建模板
【克隆模板】
选择需要克隆的模板对应的cola.build并右键
点击克隆模板
编辑阶段
导入模板
其中包括通过关键字搜索模板,搜索结果可以直接打开(如本地存在),也可以选择导入。
导入时,我们可以选择针对单模板的导入,也可以通过应用名+业务名的方式检索到相关的所有模板,一并导入。
模板导入过程中,会挨个查询各个模板的所有版本号,已经对应的内容,将这些内容作为Git记录,加以提交。对于正式版本号,会打上形如: release/templatename/templateversion这样的tag。
模板依赖管理
目前IDE中并不支持编译基础组件,仅支持引用基础组件。如需编辑基础组件需在模板平台上操作,然后本地更新元数据获取最新版本号后更新依赖以生效。这是因为,我们希望未来Import可以由Template标签来替代,Template相关的编辑,引用和编译IDE中都是支持的。
因而,这里只包括基础组件的依赖管理以及批量版本变更。
对于基础组件的依赖管理,实际上是可视化地编辑cola.build中的dependencies字段。
批量版本变更则适用于一个基础组件被一组模板引用时,批量地更新这些引用模板对于被引用的基础组件的版本依赖描述(源代码),通过选择预发布,我们也可以在更新源代码的同时,将引用模板批量预发布。
类似基础组件依赖的批量修改,IDE也支持了Slot引用的批量修改。
代码帮助
此处主要包括事件链格式化、表达式格式化、本地文件搜索、以及代码样例搜索。
其中本地文件搜索可通过简单的规则精准搜索到文件如并点击打开,如s/s/m可匹配到: sub_main/src/mock.json。
代码样例搜索可通过关键字搜索到当前Aone代码库(Gitlab)上有哪些引用,如@getEngineStorage结合event_chain.json可以了解到时间链中getEngineStorage的具体用法。
表达式支持
此处的主要作用是实现表达式的一键格式化。
事件链可视化
此处主要是通过可视化的方式将事件链描述的原子能力的调用与转移表现出来,目前支持了多种转移关系,包括显式的转移以及隐式(表达式)的转移关系。
点击节点或者边也可以自动选中对应的代码区域。
代码智能感知
- 代码补全: 支持main.xml中的控件名,属性,属性值;event_chain.json中的原子能力名,原子能力参数,事件链节点;表达式(名称/data字段/事件链名称)等
- 悬浮文档: 支持main.xml中的控件名、属性名;event_chain.json中的原子能力名,原子能力参数;表达式名;
- 代码诊断: 包括main.xml中的控件名,属性名,属性值;event_chain.json中的原子能力名(原子能力参数暂不支持,主要原因是平台上的约束不够完备);表达式名;
- 跳转至定义: 主要是跳转到mock.json(即data表达式,从main.xml或event_chain.json)和event_chain.json(从main.xml);
- 代码格式化: 包括main.xml的格式化,event_chain.json的格式化,表达式格式化;
- 代码折叠: 包括main.xml, event_chain.json, mock.json以及表达式的折叠;
- 代码高亮: 主要指的是表达式名的高亮。
调试阶段
预览
这里主要包括调试服务扫码、预览页单模板预览和业务容器多模板预览。开发者通过扫码连接调试服务,当有设备连接时,当前选中的模板的变更将会编译并将产物实时推送到手机并生效,当前没有设备连接时,模板变更会实时编译,并将编译产物以二维码方式透出,可直接扫码预览。
其中预览页单模板预览同之前模板平台。
业务容器多模板的预览核心是解决预览页预览的一些问题,包括缺少对于自定义组建的良好支持,只能预览单模板,数据Mock不真实等,使用这一特性,开发者可以在任意包含DX模板的页面中预览IDE中的模板文件,代码变更并同步到设备成功后,刷新页面重新渲染模板即可生效。
这里也包括通过Command+Shift+P唤起命令面板,执行切换预览模板从而快速切换到指定模板的功能(仅预览页预览下有效)。
视图审查
视图审查主要用于排查布局显示上的问题,比如节点丢失,属性值不对等。
假设已经扫码连接了调试服务,下同,不赘述。
- IDE上打开启用视图审查开关
- 设备侧渲染DX模板
- IDE上选中某条视图审查记录(也支持模糊搜索)
- 打开对应模板的main.xml文件
- 点击main.xml中的某个标签,可以自动展开三棵树中的对应节点,并且将设备侧的对应视图高亮。
- 点击展开树节点,或者展开树节点属性节点,即可自动选中对应的源代码。
表达式回放
表达式回放主要用于排查复杂表达式执行的问题。
- IDE上打开启用表达式回放开关
- 设备侧渲染DX模板
- IDE上选中某条表达式回放记录(也支持模糊搜索)
- 点击回放按钮,即可看到整个表达式执行的时序
- 选中某个被执行过的节点,可以看到其执行的上下文。
事件链回放
事件链回放主要用于排查事件链执行的问题。
- IDE上打开启用事件链回放开关
- 设备侧渲染DX模板
- IDE上选中某条事件链回放记录(也支持模糊搜索)
- 点击回放按钮,即可看到整个事件链执行的时序
- 选中某个被执行过的节点,可以看到其执行的上下文。
设备管理
主要用于同iOS模拟器(Mac上)的深度融合,这样开发者即使没有一个iOS设备,或者设备商没有可用于测试的包,也可以方便地安装并使用IDE的各个功能。
发布阶段
模板发布
- 选择需要发布的模板(支持搜索)
- 选择需要发布的分支
- 填写发布描述
- 选择部分配置项(其中校验版本冲突主要用于防止模板平台上存在一些更新的版本号;预先提交变更可使开发者讯速地修改校验变更(尤其是简单模板的变更);跳过Git检查可以不用检查是否有待提交的内容,尤其是当开发者需要发布另一个分支,且当前分支的变更不需要提交时;预发布则用于配置是否正式发布)
- 发布即可
发布的结果弹窗也会提示开发者这次发布产生了哪些版本号,产物cdn地址等。
模板历史版本查询
这里包括: 模板url拷贝、模板zip下载、模板预览、发布信息、源代码查看、代码Diff、内容回滚、Diff链接分享(CR使用)等。
批量内置
这里将会完成双端产物的内置,以及Android侧所需要的presetTemplateInfos.json构建。
设置
这里主要是Gitlab Token的配置,以及基于GitDiff的模板发布所以来的Diff基线配置。
IDE演进思考
针对过去模板平台的不足,我们实现了从编辑器到IDE的转变,显著提升了开发者的研发体验。面向未来,我们有以下方面的演进思考:
扩大应用场景/团队
IDE直接收益
- 工程化支持,开发者可在一个工作区内开发相关业务的一组模板;
- 代码托管到Gitlab,多人协同,提交历史,回滚,搜索,CR,数据统计,直接打通集团代码服务;
- 更简单的批量模板管理,如批量修改基础组件依赖,批量发布,批量内置等;
- 对标高级语言编辑的代码智能感知体验(补全,文档,诊断,跳转,格式化,折叠,高亮等),涵盖main.xml, 表达式,事件链等。不仅如此,针对事件链,IDE还提供排序,一键折叠所有和可视化预览;
- 更好的预览体验,开发者可结合IDE的业务容器预览直接在当前业务容器动态替换模板(IDE中模板即时修改编译生效);
- 丰富的问题排查工具箱,针对视图异常排查,IDE提供了视图审查和源代码关联,可以建立动态的源码同实际视图结构(属性)的双向关联;针对表达式执行复杂的问题,IDE提供了表达式回放以跟踪表达式AST的结构和,执行顺序,输入输出和上下文;针对事件链执行复杂的问题,IDE提供了事件链回放以跟踪事件链中原子能力之间的执行顺序,输入输出和上下文;
- 更好的模拟器融合,Mac上IDE深度融合了iOS模拟器,开发者可快速安装/激活应用,连接调试服务并预览页面。
IDE潜在收益
- DX IDE提供了研发模式层面的扩展,基于DX衍生出来的技术方案可便捷地融合到IDE;
- 目前我们正在移动小组层面同研发体验CoE团队讨论客户端研发效能的度量模型和指标,基于IDE对于研发过程的多维度细粒度感知,我们将有机会帮助团队度量研发效能,进一步改善研发体验。
IDE使用成本
- 开发者需安装O2和DX IDE插件,之后会有自动化的脚本,工具和命令以保证环境符合要求;
- 既有模板迁移成本的问题,目前IDE支持了基于搜索关键字(或ID)的单模板导入,以及基于app&biz的一组业务模板导入,一键迁移所需模板到DX工程;
技术迭代
目前,IDE已实现DX工程化和全研发生命周期支持,包含优秀的代码编辑体验,诸多问题排查手段,丰富的工具箱以解决研发痛点,后续我们将围绕以下方面开展技术迭代。
既有设计的完善
- 补齐Android SDK中视图审查功能。
- 性能方面,提供包括编辑期Linter,运行期的实时性能采集,发布期的Benchmark,运维期的性能大盘等数据以改善模板性能。
- 兼容性方面,打通手机中台等设备服务,开发者可以快速预览在不同设备上的渲染效果和性能。
技术演进与探索
- 目前的技术架构下,开发者依需安装IDE和插件,存在接入成本高的问题。通过将IDE远程部署,开发者使用Web连接远端服务,不仅可避免安装成本,也便于共享工作区等;
- 过去客户端业务研发往往是客户端与服务端分离,面对不断增加的业务挑战,更快的迭代速度要求,以业务为中心的端云一体化开发有了更多价值,Faas本身的日益成熟也使得客户端开发后端逻辑更为便捷可行。IDE作为一个统一的编程平面,能更好地支持一体化工程下的前后端逻辑(页面)开发,加速业务价值交付;
- 目前业界主流生态越来越多地使用声明式UI,不论是技术先进性还是技术同学成长,我们都应更多地拥抱这一趋势。探索DX DSL同声明式UI(JetpackCompose, SwiftUI,Flutter,ArkUI)的结合,通过升级DSL,结合Jetpack Compose/SwiftUI/Flutter,配合以相关的分析解析编译执行等工具链配套,开发者可更贴近原生(友好)地开发跨平台业务。