🤡 低代码技术之编辑器与渲染器解耦

609 阅读3分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第3天,点击查看活动详情

名词解释:

编辑器: 低代码中的编辑器可以理解为左侧的物料区域(组件库)以及右侧的属性编辑区域。

渲染器: 低代码中的渲染器可以理解为中心区域的画布区,用来呈现你编辑后的页面效果。

如图所示:

红色为编辑器,蓝色为渲染器 image.png

原先的做法思路:

image.png

可以看到,我们红框区域是一层遮罩层,而绿色区域就是渲染层。

而在遮罩层,咱们做的事情就可以有很多了,比如拦截拖拽操作,从左侧物料区域拖拽至中心区域后,告知渲染层去渲染组件,与选中组件操作,当我们在遮罩层选中组件后,告知渲染层当前选中了哪个组件,等等操作。

原先做法的实现:

(1)获取渲染器中所有的物料组件

(2)在遮罩层上,动态生成组件对应的的div元素,可以理解为其实每一个组件在遮罩层中,就是一个div。(给你一个假象,你在拖拽移动的时候,是在操作这个组件,其实是在操作这个div)

(3)选中时,动态添加选中框样式。如图所示: image.png

(4)设置遮罩层zIndex大于渲染层的zIndex。

原先做法的总结:

可以看到,我们确实是完全实现了画布的组件编排能力,但是,渲染器和编辑器完全耦合在一起,如今我的平台是vue3技术栈,这也意味着我的渲染器也就是vue3技术栈,那如果今天,我们需要去兼容vue2,react的平台能力,就会愈加麻烦。

那我们该如何去解决这个问题?这里我们就要引入一个运行时的概念,顾名思义,这个运行时就是渲染组件组成的运行环境,它应该独立于我们的编辑器区域,而不是依赖。

调研:

参考了业界有名的低代码平台,如阿里的低代码引擎,腾讯的tmagicEditor,他们都完完全全实现了编辑器与渲染器的解耦,

image.png

image.png

阿里的解耦做的更狠一些,直接把遮罩层的能力也解耦开来了,而腾讯保留了遮罩层能力。

可以明显看到,都使用到了iframe做运行时,这里我们也使用iframe做一个运行时,即渲染器,接下来,我们就来探讨下如何进行实现。

思路:

首先,我们如何去实现编辑器与渲染器的通信能力,同学们很快就想到了,使用iframe的postMessage事件,但我深入了腾讯的低代码引擎源码,发现了一个黑科技操作,在iframe的window中定义一个属性,在它加载后,赋予contentWindow上的该属性一个值,当iframe执行script的时候,就可以拿到这个值。

简单实现:
el.appendChild<HTMLIFrameElement>(iframe);
iframe.contentWindow.属性 = 值

不过这两行代码我卡住了很久,我思考为什么iframe在执行的时候能拿到这个动态赋予的值呢?,我把两行代码顺序换一下,就会报错contentWindow没这个属性。而appendChild又是一个异步事件,iframe中的script脚本执行时机,我们并不知道。如果评论区有小伙伴知道的话,可以评论一下!

具体实现:

编辑器

el.appendChild<HTMLIFrameElement>(iframe);

iframe.contentWindow.runtimeApi = {
    emitRuntimeReady: (runtime: any) => {
        window.runtime = runtime;
    },
}

渲染器

window.runtimeApi.emitRuntimeReady({
  add(v: any) {
  },
  update(config:any) {
  },
  remove(id: string) {
  },
  clearAll() {
  },
});

这样,在编辑器加载iframe后,便能拿到渲染器的运行时,当编辑器在进行某些操作时候,就可以去告知渲染器去进行动态渲染。基于此,我们也做到了完全的解耦,可以有3、4套不一样的运行时,因为对于编辑器而言,只是一个iframe的url的差别,然后就可以获取不同运行时的操作api。

感谢观看!