Skip to main content

技术架构概览

我们在 2023年8月底正式开源了 Tango 低代码引擎。Tango 是一个基于源码的低代码设计器框架,支持直接基于项目源码提供低代码可视化开发能力,可以无缝的与既有的本地开发工作流进行集成,从而提供渐进式的低代码开发能力。

Tango 低代码引擎使用演示

按照计划,我们在 2023年9月底发布了 1.0 alpha 版本,在此版本中我们遵循 “最小内核” 的原则对 Tango 的核心实现进行了大幅的重构,剥离了大量冗余的代码实现。

为了帮助大家更近一步的了解 Tango 开源版本的核心构成与代码实现,本文将会详细揭秘 Tango 低代码引擎的设计思考与实现过程。

低代码可视化搭建之殇

从实现上看,低代码搭建能力的核心是 UI 可视化编程。借助 UI 可视化编程,可以大大的弱化使用者对于代码编程的感知,但在真实的业务需求场景中,我们面临着大量的复杂的应用逻辑,使用者很难借助 UI 操作表达功能逻辑。例如下图中的合同管理,资金结算等页面。如果借助于传统的低代码方案,通常会发现,很容易一条路走到黑,没有回头路。所以,经常会有开发者抱怨,稍微复杂的场景下,低代码的效率甚至不如写代码。

在实际业务场景中面临大量难以低代码开发的前端应用

传统低代码方案的问题

我们不妨先简单分析一下传统的低代码方案的问题。传统的低代码搭建方案往往采用定义私有 Schema 协议来可视化表达视图逻辑,也就是将代码逻辑转换为私有的描述,大致的原理可以参考下面这张图。

基于 Schema 的低代码可视化搭建方案

这类方案很容易面临不断膨胀的私有 JSON 协议。并且,私有协议扩展性和灵活性差,难以达到图灵完备状态。例如在我们的实际开发过程中,传统的低代码方案会面临各种各样的扩展性卡点。此外,开发能力往往受限于内置的组件和模板。且难以复用现有的前端资产,例如组件和代码等等。对于开发者而言,私有协议也导致问题定位难,调试难。

借助于私有协议的搭建方案通常适合于轻业务逻辑的简单类表单,营销类的活动页面等等,很难用于复杂的业务逻辑搭建场景,因为私有协议难以有效的应对这类场景的复杂性和灵活性需求。虽然,有些方案提供了协议转代码的能力,但通常只实现了单向转码,可视化开发和代码开发是两条完全割裂的路径。

在此基础上,我们就需要重新思考低代码搭建协议的设计问题。

从私有搭建协议到公有协议

那么,我们能否不使用私有协议,而是采用公有协议?

答案是,可以的!ESTree 规范作为主流的处理 JavaScript 源代码的标准社区协议,被广泛用于浏览器 JavaScript Parser 的实现。借助于 ESTree 协议,可以完美的实现对源码逻辑的描述,并且社区有大量的工具可以帮助我们完成这个过程。

基于ESTree规范,实现双向互转的低代码搭建能力

因此,我们尝试使用 ESTree 规范来实现低代码搭建过程。借助于 ESTree 规范,我们无需定义私有的渲染描述协议,并且可以低成本的实现代码到协议,协议到代码到互转。借助于双向转码的能力,我们获得全新的低代码开发体验。

Tango 低代码引擎实现原理

基于这个思路,我们设计了基于 ESTree 规范的低代码引擎方案 -- Tango。可以通过下面这张图来简单的描述下实现逻辑:

Tango 低代码引擎实现分析

首先将源代码解析为 AST。用户的拖拉拽等操作则映射为对 AST 的遍历和修改。最后将新的 AST 重新生成代码,交给设计器沙箱去渲染执行。而对 AST 的解析、遍历、修改、生成,则可以借助大量的社区工具,这里我们选择的是 babel!

AST 的全称是抽象语法树,是一种分层的程序表达,根据编程语言的语法呈现源代码的结构。

大量的工具基于 AST 实现

其实,数量众多的前端工具库都是基于 AST 操纵实现的。我们可以发现,在任意的前端项目中的 package.json 里的 devDependencies 里的很多工具包是基于 AST 解析操纵实现的,例如 JS 的转译,代码压缩,ESLint 等等,我们可以阅读这些工具的源码来进一步的学习。

将源码转为 AST 描述的基本过程

如图所示,将源代码转为 AST 描述的基本过程包括词法分析和句法分析两个阶段:

  • 词法分析:借助词法分析器将代码字符串分割为标记列表。
  • 句法分析:借助句法分析器将标记数据转为 AST 描述。

最后,我们可以获得源代码的结构化描述树。有很多工具可以帮我们来实现这个过程,例如 babel -- 它可以帮助我们轻松的实现代码到 ast,ast 遍历修改,ast 到代码的过程。

基于 AST 实现搭建的基本过程

我们来看一下使用 ast 实现搭建逻辑的基本过程。

看一个具体的例子:通过修改 AST,在 Page 中插入一个 Section 节点。

基于 AST 实现搭建逻辑

中间这段代码,展示了核心的逻辑,通过遍历整个 AST 中的所有 JSXElement 节点,找到第一个 Page 元素,然后在 Page 元素的 children 里插入新的 Section 节点。这只是一段演示代码,具体的过程比这个要复杂的多,因为有很多的边际逻辑要处理。最后,我们可以将 ast 重新生成为代码,得到我们想要的结果。

Tango 的数据变更流程设计

了解了基本的实现原理后,我们来看一下低代码引擎的数据变更流程设计。

数据变更流程设计

首先是引擎初始化。源码文件会被引擎内核解析进行状态初始化。接下来,对于用户的操作,会触发浏览器事件,引擎接收到相应的事件,触发内核中的状态变更,更新 AST。

然后,内核会基于新的 AST 的同步生成代码,由引擎将代码同步给渲染沙箱。渲染沙箱感知到代码变化后,会触发页面重新渲染,也就是沙箱的 HMR 过程。

基于源码的在线渲染沙箱设计

接下来,我们需要考虑的是如何在浏览器中执行 JavaScript 源码工程?有很多方案可以选择,我们选择的方案是 sandpack,它是由 CodeSandbox 开源的可以在浏览器中实时运行 JavaScript 项目的的工具库。在具体实现上,我们对 sandpack 进行了一系列的改造,以满足低代码生产环境的需要。

基于 sandpack 的在线渲染沙箱方案如下图图所示。

Tango 沙箱设计

在实现上,主要包括 3 个部分,分别是:​

  • 低代码沙箱:它是一个开箱即用的前端组件,只需要传入源代码和构建配置信息即可完成前端项目的构建和执行。
  • 在线 Bundler:是低代码沙箱的核心,用来在浏览器上构建和执行源代码,本质上是一个在浏览器端运行的简化版 webpack。
  • 打包服务:是一个 node 服务,用来对 npm 包执行预构建和资源合并。

从沙箱执行流程来看,首先 Sandbox 组件将项目的源代码和 compile 指令使用 postMessage 传递给在线 Bundler,在线 Bundler 在接收到 compile 指令后,bundler 会从 packager 打包服务加载项目的 npm 依赖,然后编译和执行代码,最后发送 success 消息给低代码沙箱。

Tango 低代码引擎的构成

结合上面的介绍,在构成上,Tango 低代码引擎主要包括 3 个核心组成部分,分别是:

  • 引擎内核:扶额建立文件,节点模型,提供输入输出能力。
  • 拖拽引擎和可视化面板:提供可视化开发能力
  • 渲染沙箱:提供源码在浏览器上的编译执行能力。

引擎构成

借助于 Tango 低代码引擎,我们可以为开发者提供全新的在线开发体验,支持源码级的自定义能力。对可视化开发而言,可视化配置会触发 AST 的修改,进而会重新生成对应的源码。而对源码开发而言,修改源码后会同步更新 AST。