一. 项目由来

19年夏天的一次小组头脑风暴会议上,成员们在讨论下半年创新项目时提出了这样一个想法,大致思路是左侧预览,右侧填充数据,数据保存后触发页面更新,为此我们还做了一个类似的DEMO进行演示。因为当时我们已经有Blade模板平台可以使用,所以该项目的优先级被排在了后面,惨遭搁置。
2020年以来因为疫情的原因,我们公司相关的营销业务越来越多,活动形式大同小异,多是单期,而Blade平台只能做到整页定制,无法复用,就导致营销活动占用了更多的开发资源,挤压了其他项目,所以我们就又想起了上面那个想法。我们需要一个能更精细化定制,可自由组合,实时预览,方便使用的模板平台去解决这个问题。

二. 明确需求

2.1 定制的颗粒度

该模板平台的主要使用对象是运营人员,开发人员负责开发组件,所以颗粒度不能太细,否则运营人员的学习成本和开发人员的开发成本都会非常高昂。每个组件最好只能配置少量参数,逻辑全部由开发人员在组件内部实现;

2.2 组件的UI

由于该模板平台生成的页面是 to C 的,所以必须是强UI表现,需由开发人员自己编写,像生成企业OA、后台那种的定制UI组件或者不支持自定义组件的就得PASS掉;

2.3 自由组合

对于这个需求,一般的可视化搭建框架都能实现。由于我们的业务都是面向移动端用户,不像PC那样需要嵌套,所以采用非嵌套组件层级规则,每个组件占宽100%,可调整先后顺序即可;

2.4 实时预览

因为之前的Blade模板平台,只有发布到测试/正式环境才能查看实际配置后的效果,遇到不确定的配置项可能得重发好几次才能符合最终需求。该需求一般的框架都能满足,所以开发排期排到最后。

三. 技术调研

本着不重复造轮子的想法,先调研一下有没有现成的解决方案。

1. amis

amis 是百度开源的一个低代码前端框架,它使用 JSON 配置来生成页面,功能很强大,能实现很多复杂的功能,但是有点不适合我们的业务。amis的目标是 toB 用户,组件很简洁(就是丑),不适合toC,并且配置项太多,学习成本很高。而我们新的模板发布平台打算是提供给运营人员使用,考虑到运营人员的技术水平约等于无,所以只能PASS,如果后续有后台的需求,我们会考虑用这个。

2. 鲁班H5

鲁班H5是基于Vue2.0开发,通过拖拽的形式,生成页面的工具。它支持自定义组件、脚本系统、拖拽等功能,能满足我们的业务需求,但是对于一些复杂组件要实现自己的编辑器,这点让我们很犹豫。试想一下自己做完一个很复杂的组件,还要去做一个相应的好用的组件属性编辑器,还要考虑各种校验等等一系列问题,令人望而却步。

3. pipeline-page

pipeline-page 是 github 上开源的一套页面可视化搭建框架。它将整个系统分为了三部分:编辑器,实际页面,后台。编辑器通过iframe与实际页面关联,编辑器编辑属性之后发给后台,后台构建页面,刷新之后即可实时预览,通过这种方式
1.实现了编辑器和组件库前端框架的分离;2.实现了编辑器和组件库各组件的分离;3.避免了预览页面的逻辑和样式污染编辑器环境。
这样的方案理论上无论采用什么技术栈,升级组件库前端框架,都可以做到成本最小化。编辑器页面只提供数据,后台负责存储组件信息、组件属性值和页面的数据,渲染页面只需要根据获取的页面和组件数据进行展示,三者解耦。另外pipeline-page 采用了 JSON Schema 的方案来实现组件属性表单的生成,并且带有校验,不用再考虑去写编辑器了,完美解决了 鲁班H5 的痛点。
看到 pipeline-page 之后就没有再去调研其他的可视化搭建框架了,这套方案的架构十分适合我们的业务,但是它的实现不太符合我们现有的业务,于是我们决定按照 pipeline-page 的思想,根据花椒前端的实际情况自己实现一套。

四. 技术架构

花椒低代码可视化编辑平台也是分为三个部分: 前端,服务端,组件库+预览页。

4.1 前端部分

前端采用的是D2Admin的模板搭建了一个简易的管理系统,根据花椒自己的实际业务增加了团队管理,站点管理(预埋,还未实现),页面管理,组件分类等功能。在编辑页面我们在左侧按照不同分类展示组件,点击可弹出浮层介绍组件详情,方便运营同学了解使用方法,点击添加即可在中间预览区看到组件;右侧为属性编辑区,组件的逻辑和样式由组件内部封装,前端部分通过组件信息中的 JSON Schema 去渲染表单,运营人员根据开发人员提供的简单的预设属性来填入信息即可。使用组件搭建出一套活动页面,例子: [https://web.huajiao.com/jimu/3/index.html](https://web.huajiao.com/jimu/3/index.html)

低代码可视化编辑平台的设计与实践 - 图1编辑页

前端部分与 pipeline-page 不同的是我们在编辑组件属性时,不仅将组件及页面信息发送给后台服务端,而且同时将这些信息通过 window.postMessage 的方式传输给iframe中的预览页;预览页本身集合了所有业务组件,收到信息后通过 动态组件 的方式实时渲染,方便了使用人员及时看到配置后的效果,不需要后台构建,比 pipeline-page 的预览更加快速及时。所以在页面未发布之前,不会生成真正的页面,它只是存在服务器端中的一段数据。
低代码可视化编辑平台的设计与实践 - 图2
其中 page 字段是对页面本身的一些描述信息,componentList存储的是页面使用的组件中的数据,预览页使用 cname 指定渲染的组件,style 字段为组件容器的样式(用于调整组件的显示区间等不影响组件功能的样式),props 就是组件根据业务需求需要填入的属性值(VUE Compoent中的props字段需要传入的值)。

4.2 服务端部分

服务器负责存储前端提交的组件数据、页面数据以及组件信息,在收到用户发起的发布请求后,通过调用gitlab的API来触发pipeline构建和发布页面。
技术架构:
1.node框架基于可维护可扩展的考虑,采用的是nestjs框架。2.使用管道、DTO验证参数,摒弃if-else,使参数验证更加优雅。3.用户注册登录是基于passport-jwt实现,再配合nest的路由守卫方便进行用户信息的验证。4.使用typeorm连接数据库,可以让前端专注于业务逻辑,不用过度担心数据存储。
服务器在以上技术的基础上,实现了和各个平台的联动。详情如下
低代码可视化编辑平台的设计与实践 - 图3

4.3 预览+编译部分

我们的实现方案和 pipeline-page 最大的不同就是在 预览+编译部分,我们觉得 pipeline-page 方案关于组件这部分太过割裂,使用起来不顺畅,用户还需要打包并将资源转移到 pipeline-resources 中,所以我们依托花椒的GITLAB CI/CD 实现了一套”预览+编译”功能。
组件库目录结构:
低代码可视化编辑平台的设计与实践 - 图4
组件库和平时开发的单页项目没有任何区别,开发人员切新分支后在 components/xxx 分类目录下添加组件,组件可以引用公共资源。
分类的目录结构
低代码可视化编辑平台的设计与实践 - 图5
class.config.json 标识子分类信息
autoplay-video/index.vue 为模板文件
低代码可视化编辑平台的设计与实践 - 图6
这个和平常开发一个业务组件是没有任何区别的,props 中标明了组件所需数据,每个属性上面必须写好注释,因为我们写了一个脚本工具,执行 yarn generate:schema 命令即可将 props 通过脚本自动转化为渲染表单的JSON Schema => autoplay-video/index.schema.json, 当然这个文件开发人员也可以选择不自动生成,自己手写也可以;
然后开发人员在 constants/devConfig.js 中编辑 componentList 即可实现本地调试;
低代码可视化编辑平台的设计与实践 - 图7
开发测试完毕之后合并到master分支,此时通过CI执行添加组件脚本 node scripts/updateComponent.js,将组件信息提交给服务端,对开发流程无侵入,和平时开发无区别。

  1. components_update:
  2. image: registry.huajiao.com/gitlab-ci/dplt-core:0.1.5
  3. stage: components_update
  4. script:
  5. - node scripts/updateComponent.js
  6. only:
  7. changes:
  8. - src/js/components/**/*
  9. refs:
  10. - master
  11. except:
  12. - triggers

添加组件之后需要将新的组件集合到预览页中,同样是通过CI文件执行新的脚本构建和发布新的预览页。

  1. build_preview:
  2. stage: build
  3. script:
  4. - PREVIEW_MODE=true yarn build
  5. artifacts:
  6. name: "$CI_COMMIT_SHA" # 每次提交一个功件
  7. paths:
  8. - dist
  9. when: on_success
  10. only:
  11. refs:
  12. - master
  13. except:
  14. - triggers
  15. deploy_preview:
  16. image: registry.huajiao.com/gitlab-ci/dplt-core:0.1.5
  17. stage: deploy
  18. dependencies:
  19. - build_preview
  20. script:
  21. - PREVIEW_MODE=true node scripts/deploy.js
  22. only:
  23. refs:
  24. - master
  25. except:
  26. - triggers

待发布完毕之后,在编辑器页面调用新组件的时候就可以显示出来了。

  1. if(PREVIEW_MODE === 'true') {
  2. window.addEventListener("message", (event) => {
  3. console.log('onMessage', event.data);
  4. if(
  5. typeof event.data === 'string'
  6. && event.data !== ''
  7. ) {
  8. const message = JSON.parse(event.data);
  9. if(message.identifier === 'jimu_preview') {
  10. if(message.type === 'updateView') {
  11. safeStart(); // 这里是清理组件重新初始化VUE
  12. loadConfig(message.data); // 这里是加载上面的预览信息
  13. }
  14. }
  15. }
  16. }, false);
  17. }

发布生成真正的文件同样是依托 gitlab CI 功能,代码如下

  1. build_template:
  2. stage: build
  3. script:
  4. - yarn build:trigger
  5. artifacts:
  6. name: "$CI_COMMIT_SHA" # 每次提交一个功件
  7. paths:
  8. - dist
  9. when: on_success
  10. only:
  11. - triggers
  12. deploy_template:
  13. image: registry.huajiao.com/gitlab-ci/dplt-core:0.1.5
  14. stage: deploy
  15. dependencies:
  16. - build_template
  17. script:
  18. - node scripts/deploy.js
  19. only:
  20. - triggers

当用户点击发布时,服务端调用 [https://git.huajiao.com/api/v4/projects/772/trigger/pipeline](https://git.huajiao.com/api/v4/projects/772/trigger/pipeline) 接口,将构建页面所需信息传输过去,CI接收到信息后将其进行格式化并组装成自己所需信息,然后构建页面并发布到测试环境/正式环境。

  1. if(process.env.DEPLOY_CONFIG) {
  2. // 从环境变量取传入配置
  3. const DEPLOY_CONFIG = JSON.parse(process.env.DEPLOY_CONFIG);
  4. config = {
  5. componentList: DEPLOY_CONFIG.components.map(item => {
  6. return {
  7. id: item.cname,
  8. style: JSON.parse(item.style),
  9. props: JSON.parse(item.props)
  10. }
  11. }),
  12. page: {
  13. id: DEPLOY_CONFIG.pageId,
  14. name: DEPLOY_CONFIG.pageName,
  15. ...JSON.parse(DEPLOY_CONFIG.props),
  16. }
  17. };
  18. }

前端可以通过服务端转接的 gitlab 接口轮询 pipeline 状态,给用户显示发布进度信息。

  1. {
  2. "id": 161337,
  3. "sha": "50e37a230e1f221ae1fdd9b28cf27d3c7eb1f9bb",
  4. "ref": "master",
  5. "status": "pending", // 这里的状态
  6. "web_url": "https://git.huajiao.com/frontend/cli/jimu/pipelines/161337",
  7. "before_sha": "0000000000000000000000000000000000000000",
  8. "tag": false,
  9. "yaml_errors": null,
  10. "user": {
  11. "id": 18,
  12. "name": "yyy",
  13. "username": "zzzxxx-hj",
  14. "state": "active",
  15. "avatar_url": "https://secure.gravatar.com/avatar/39fe51fea0fca93e688cbae09d68b30f?s=80&d=identicon",
  16. "web_url": "https://git.huajiao.com/zzzxxx-hj"
  17. },
  18. "created_at": "2021-07-01T09:29:00.210Z",
  19. "updated_at": "2021-07-01T09:29:00.332Z",
  20. "started_at": null,
  21. "finished_at": null,
  22. "committed_at": null,
  23. "duration": null,
  24. "coverage": null
  25. }

五. END

至此花椒的这套可视化搭建框架大致讲解完毕,功能虽然还比较简陋,但它能切实解决目前我们前端组所遇到的难题,并且由于是我们自己组内开发,可以自由拓展符合公司业务的功能;这套框架的编辑器和组件库之间只通过iframe相关联,以前业务中积累的组件可以快速添加到该组件库中,进而丰富组件库,满足业务方需求。