背景
我们服务于政务行业,正在打造一个集代码开发、数据集成、应用管理、一体化运维监控的应用支撑平台。
以此为导向,作为开发的第一步,代码工程创建应当为后续的集成、管理及监控等服务。所以区别于一般的cli工具,我们要做的工具还有以下特点:
1、支持在web页面进行脚手架参数配置,生成工程代码框架,以便将创建的工程与应用关联进行管控
2、除了cli工具常规的babel、less、sass之类基础功能,需要支持一些政务中的常用功能如监控、日志、水印等,还有前端微服务框架
3、部分模块支持二级配置,如微前端需配置主/子应用,水印需配置内容,监控需配置应用的标识等等
技术难点
我们常用的cli工具,如vue和react的cli工具,基本都是静态模版,即准备几套工程模版,如移动端一套,pc端一套,根据参数拉取对应的代码模版即可。
而以我们的功能模块数量和参数数量,显然无法采用这种方式,需根据配置实时生成工程代码。
以下是我们需要解决的问题:
1、如何将某个扩展模块特有代码文件或者代码片段注入工程,并在未选择该模块时避免这些代码污染工程
2、不同扩展模块修改同一个文件避免冲突、依赖相同npm模块时避免重复注入
3、最后呈现的代码需简洁优雅,为最佳实践,不能将对工程配置的判断语句带到生成的工程代码中
4、对接web页面配置并下载工程代码等
5、支持扩展模块的新增及优化,bug修复等,并支持在不升级cli情况下修复扩展模块的bug
6、cli及扩展模块物料的版本管理,如何支持各自修复bug同时又保证版本一致性
解决方案
针对问题1,我们将模版目录分解为公共资源目录、模块资源目录,其中模块目录下的资源文件仅在选择该模块后才整合到工程中。
针对问题2、3,我们引入ejs模版,对.ejs后缀的资源文件进行模版编译,这样可以通过模版的逻辑语法处理冲突,模版代码也会在编译后消失。且模版中能清晰看到各模块注入的语句,也能很好避免重复注入。
针对问题4,我们在模块中增加模块参数配置文件,并启动一个nodejs的wen服务,将配置返回到页面,且实现一个动态表单(这个后面有时间我单独推荐一下,基于tdesign-vue,代码不多,但非常方便),将配置与页面UI对接。web服务接收到页面配置后将调用工程创建模块创建代码工程并下载
针对问题5,我们将模版资源和cli分开,在不同仓库中维护,并将模版物料资源推送到COS,cli从COS拉取模版物料,且物料支持版本号
针对问题6,我们制定版本规范,物料git仓库分支对应物料版本,这样修复特定版本物料的bug不会影响其他不使用该版本物料的cli版本。
代码生成方案
目前我们的模版结构如下图:
template-source为公共代码,会全部生成到创建的工程中。modules下为扩展的可选模块,该模块被选择时会将其source目录下文件拷贝入工程,目录也是对应的。
最开始我们准备在代码中插入代码片段标识,然后让代码生成时将标识替换为对应代码,并将未配置的功能模块的标识删除。但我们很快就放弃了这一方案,因为这种标识太不直观,要看对应的代码是什么还得去对应模块去查定义,多人合作时你根本不清楚别人的代码与你的是否存在重复依赖,变量重名等问题,而且灵活性也不够,即使看出问题也很难处理。
放弃此方案后,我们选用ejs模版引擎进行代码注入。不管是公共代码还是模块特有代码,也不管是js、css、json、vue,需要时都可以添加.ejs后缀,让代码生成器对其做ejs模版编译,方案非常通用和简洁。
可以看出,该方案下各模块插入的代码一目了然,冲突很容易避免,并且模块下的子参数也能方便地进行判断和处理。
最后,我们实现了一个简单的插件方案,工程创建的开始、编译、结束都提供相应回调,通过插件可在这些周期中插入自己的逻辑,这可作为兜底,支持个别模块特定情况下移除多余目录之类的操作。
web页面对接方案
我们启动一个nodejs的web服务来对接web页面
web服务从资源管理模块获取扩展模块及模块子参数的信息,返回到页面,页面选择模块并配置好参数后,调用工程创建接口创建和下载工程代码。
在define.js中定义模块子参数
页面上通过动态表单组件渲染参数配置,如下:
工程在线预览方案
不同于静态的代码框架,我们代码是根据配置实时生成的,所以无法预先启动一个预览工程。要实现在线预览,只能在根据配置创建完工程后启动一个新的服务用于预览。由于部署后启动的预览服务端口没有暴露,所以需要一个预览接口通过代理预览服务。
这块资源代理,启动的webpack的publickPath需要对应部署后的网关路径,不然js等资源无法被代理。不过这些只是繁琐,真正的卡点在性能。已采取多种优化措施,比如依赖预先安装、vue和tdesign替换为cdn,受限于开发环境服务器性能,部署后预览通常要30秒以上,已准备暂时屏蔽该功能。
版本管理方案
为灵活维护,我们将cli和模版放到不同的代码仓库中。开发时,物料作为子仓库引入,cli直接从本地代码获得模版代码;然后物料按版本推送到COS对象存储,cli部署后就从COS中下载对应版本的模版物料。
优化方向
在线预览是下阶段优化的重点,我们有几个方案:
1、预先启动一个包含全量扩展模块的服务,然后该服务需实现根据配置动态屏蔽部分模块功能。这种方案不需要实时启动预览服务,但无法根本上保证启动的预览与选取的配置一致,可能存在差异。
2、使用vite替代webpack启动服务。vite不需要预编译代码,启动非常快。该方案已初步调研,验证启动速度必定让人满意,但仍然有其他不足,一是虽然启动时不编译,但首次加载页面仍然有实时编译,会导致预览页面加载较慢;二是vite替换webpack有兼容问题,也同样无法保证表现上完全一致。如果该方案能成功,后面我单独写一个webpack到vite的切换方案。
最后,感谢一起设计、开发和完善该工具的同事 colinczhu、damonxshi。