花椒低代码可视化编辑平台的设计与实践

程序员成长指北 2021-07-23 08:47

点击上方 程序员成长指北,关注公众号

回复1,加入高级Node交流群

一. 项目由来

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


编辑页


前端部分与 pipeline-page 不同的是我们在编辑组件属性时,不仅将组件及页面信息发送给后台服务端,而且同时将这些信息通过 window.postMessage 的方式传输给iframe中的预览页;预览页本身集合了所有业务组件,收到信息后通过 动态组件 的方式实时渲染,方便了使用人员及时看到配置后的效果,不需要后台构建,比 pipeline-page 的预览更加快速及时。所以在页面未发布之前,不会生成真正的页面,它只是存在服务器端中的一段数据。

其中 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连接数据库,可以让前端专注于业务逻辑,不用过度担心数据存储。

服务器在以上技术的基础上,实现了和各个平台的联动。详情如下

4.3 预览+编译部分

我们的实现方案和 pipeline-page 最大的不同就是在 预览+编译部分,我们觉得 pipeline-page 方案关于组件这部分太过割裂,使用起来不顺畅,用户还需要打包并将资源转移到 pipeline-resources 中,所以我们依托花椒的GITLAB CI/CD 实现了一套"预览+编译"功能。

组件库目录结构:

组件库和平时开发的单页项目没有任何区别,开发人员切新分支后在 components/xxx 分类目录下添加组件,组件可以引用公共资源。

分类的目录结构

class.config.json 标识子分类信息

autoplay-video/index.vue 为模板文件

这个和平常开发一个业务组件是没有任何区别的,props 中标明了组件所需数据,每个属性上面必须写好注释,因为我们写了一个脚本工具,执行 yarn generate:schema 命令即可将 props 通过脚本自动转化为渲染表单的JSON Schema => autoplay-video/index.schema.json, 当然这个文件开发人员也可以选择不自动生成,自己手写也可以;

然后开发人员在 constants/devConfig.js 中编辑 componentList 即可实现本地调试;

开发测试完毕之后合并到master分支,此时通过CI执行添加组件脚本 node scripts/updateComponent.js,将组件信息提交给服务端,对开发流程无侵入,和平时开发无区别。

components_update:    image: registry.huajiao.com/gitlab-ci/dplt-core:0.1.5    stage: components_update    script:        - node scripts/updateComponent.js    only:        changes:            - src/js/components/**/*        refs:            - master    except:        - triggers

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

build_preview:    stage: build    script:        - PREVIEW_MODE=true yarn build    artifacts:        name: "$CI_COMMIT_SHA" # 每次提交一个功件        paths:            - dist        when: on_success    only:        refs:            - master    except:        - triggers
deploy_preview: image: registry.huajiao.com/gitlab-ci/dplt-core:0.1.5 stage: deploy dependencies: - build_preview script: - PREVIEW_MODE=true node scripts/deploy.js only: refs: - master except: - triggers

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

if(PREVIEW_MODE === 'true') {        window.addEventListener("message", (event) => {            console.log('onMessage', event.data);            if(                typeof event.data === 'string'                && event.data !== ''            ) {                const message = JSON.parse(event.data);                if(message.identifier === 'jimu_preview') {                    if(message.type === 'updateView') {                        safeStart(); // 这里是清理组件重新初始化VUE                        loadConfig(message.data); // 这里是加载上面的预览信息                    }                }            }        }, false);    }

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

build_template:    stage: build    script:        - yarn build:trigger    artifacts:        name: "$CI_COMMIT_SHA" # 每次提交一个功件        paths:            - dist        when: on_success    only:        - triggers
deploy_template: image: registry.huajiao.com/gitlab-ci/dplt-core:0.1.5 stage: deploy dependencies: - build_template script: - node scripts/deploy.js only: - triggers

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

if(process.env.DEPLOY_CONFIG) {    // 从环境变量取传入配置    const DEPLOY_CONFIG = JSON.parse(process.env.DEPLOY_CONFIG);    config = {        componentList: DEPLOY_CONFIG.components.map(item => {            return {                id: item.cname,                style: JSON.parse(item.style),                props: JSON.parse(item.props)            }        }),        page: {            id: DEPLOY_CONFIG.pageId,            name: DEPLOY_CONFIG.pageName,            ...JSON.parse(DEPLOY_CONFIG.props),        }    };}

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

{    "id": 161337,    "sha": "50e37a230e1f221ae1fdd9b28cf27d3c7eb1f9bb",    "ref": "master",    "status": "pending", // 这里的状态    "web_url": "https://git.huajiao.com/frontend/cli/jimu/pipelines/161337",    "before_sha": "0000000000000000000000000000000000000000",    "tag": false,    "yaml_errors": null,    "user": {        "id": 18,        "name": "yyy",        "username": "zzzxxx-hj",        "state": "active",        "avatar_url": "https://secure.gravatar.com/avatar/39fe51fea0fca93e688cbae09d68b30f?s=80&d=identicon",        "web_url": "https://git.huajiao.com/zzzxxx-hj"    },    "created_at": "2021-07-01T09:29:00.210Z",    "updated_at": "2021-07-01T09:29:00.332Z",    "started_at": null,    "finished_at": null,    "committed_at": null,    "duration": null,    "coverage": null}

五. END

至此花椒的这套可视化搭建框架大致讲解完毕,功能虽然还比较简陋,但它能切实解决目前我们前端组所遇到的难题,并且由于是我们自己组内开发,可以自由拓展符合公司业务的功能;这套框架的编辑器和组件库之间只通过iframe相关联,以前业务中积累的组件可以快速添加到该组件库中,进而丰富组件库,满足业务方需求。
如果觉得这篇文章还不错
点击下面卡片关注我
来个【分享、点赞、在看】三连支持一下吧

   “分享、点赞在看” 支持一波 

推荐阅读