简介
低代码平台:是通过少量代码或无需编码就可以快速生成应用程序的开发平台。通过可视化进行应用程序开发的方法,使开发人员可以通过图形化的用户界面,使用拖拽组件和模型驱动的逻辑来创建网页和移动应用程序。
- 1980年后:快速应用开发工具
- 2000年后:可视化编程语言
- 2014年后:Forrester低码概念的引入
- 2021年后:数字化建设&疫情大环境的推动
竞品分析
开源平台:
- 腾讯开源:tmagic-editor(star 2.9K) 编辑器:https://tencent.github.io/tmagic-editor/playground/index.html#/ 文档:https://tencent.github.io/tmagic-editor/docs
- 阿里开源(宜搭):lowcode-engine(star 8K) 编辑器:https://lowcode-engine.cn/demo/index.html 文档:https://lowcode-engine.cn/
- 百度开源(爱速搭):amis(star 12.1K) 前端编辑器:https://aisuda.github.io/amis-editor-demo/#/edit/0 文档:https://baidu.gitee.io/aisuda-docs 前端文档:https://aisuda.bce.baidu.com/amis/zh-CN/docs/index
设计思想
低代码开发平台在设计思想上主要可以分为表单驱动和模型驱动两种。
- 表单驱动将页面的表单和数据的存储结构合二为一,而模型驱动则与纯代码开发思想接近,实现了数据与表现的完全分离。
- 表单驱动的典型代表:Airtable、阿里宜搭、字节飞书多维表格、轻流、明道云等。
- 模型驱动的典型代表:Mendix、OutSystems、腾讯微搭、阿里Mobi、百度爱速搭、华为AppCube、网易轻舟、金蝶苍穹云、蓝卓SupOS、数睿数据等。
思考:基于元数据驱动的低代码方案?(数据驱动与模型驱动结合下的数模驱动方案?)
模块构成
我理解的低码平台构成模块分为:
低码平台可以简单分为:超管、管理端、用户端(小程序、H5、PC Web 、iOS、Android应用)
- 数据建模 元数据、字段级、对象级
- 业务建模 前端组件,拖拽,基于元数据模型驱动开发的思想,提供灵活、稳定的元数据模型建模与管理,通过数据实体、属性、关系等元数据配置响应业务需求变化,提供了在线的数据库实体建模和E-R建模功能,支持单表、一对一、一对多关系。
- 规则引擎 交互行为、事件规则、公式
- 流程引擎 工作流、审批流,数据触发、流程编排 审批流:可视化拖拉拽流程设计;会签、加签、跳转、退回、撤销等多种流程操作,配置即用;用户、部门、角色、岗位、 关系等多种选人方式,符合中国特色组织选人需求。
- 报表建模 数据工厂,BI报表、固定报表、自定义报表 报表设计器是一款在线可视化报表建模工具,提供了汇总表、明细表、柱形图、条形图、饼图、折线图、面积图、雷达图、指标图等多种种常用图表,可以组合及联动使用。设计器采用拖拽操作的风格,简单易用,能够实时展示配置效果,一目了然。
- 权限管理 数据权限、页面权限、业务权限、人员权限、管理权限
- 页面管理 模版、页面路由
- 代码管理 JavaScript、代码生成、代码调试
- 应用管理 小程序管理部署发布、版本管理
- 平台管理 API集成、云开发、微服务、容器化、私有化、公有化
实现方案
前端方案
声明式编程 vs 命令式编程
如果想绘制一个红色区块,用「声明式」来实现,可以使用 HTML CSS,类似下面的方法:
- 声明式编程
声明式UI:
Android
的Jetpack Compose
、IOS
的SwiftUi
、跨端的Flutter
、Web的Vue/React
、Web图表类的Echart
、嵌入式的Qt QML
<div style="background:red; height:50px">div>
代码语言:javascript复制const ctx = canvas.getContext('2d');
ctx.fillStyle = 'red';
const rectangle = new Path2D();
rectangle.rect(0, 0, 100, 100);
ctx.fill(rectangle);
虽然最终展现效果是一样的,但这两种代码在实现思路上有本质区别:
- 「声明式」直接描述最终效果,不关心如何实现。
- 「命令式」关注如何实现,明确怎么一步步达到这个效果。
从可视化编辑器的角度看,它们的最大区别是:
- 「声明式」可以直接从展现结果反向推导回源码
- 「命令式」无法做到反向推导
反向推导是编辑器必备功能,比如编辑器里的常见操作是点选这个红色区块,然后修改它的颜色,在这两种代码中如何实现?
如果是「声明式」的 HTML CSS,可以直接改 style
的 background
值,而基于 Canvas 的命令式代码则无法实现这个功能,因为无法从展现找到实现它的代码,命令式代码实现同样效果的可能路径是无数的,除了前面的示例,下面这段代码也可以实现一样的效果:
const ctx = canvas.getContext('2d');
ctx.beginPath();
ctx.moveTo(0, 0);
ctx.lineTo(50, 0);
ctx.strokeStyle = '#ff0000';
ctx.lineWidth = 100;
ctx.stroke();
甚至有可能这个颜色是多个字符串加随机数拼接而成,即便通过静态分析也找不到来源,从而无法实现可视化修改。
「命令式」代码无法实现可视化编辑,而可视化编辑是低代码唯一不可少的功能,所以我们可以得到结论:所有低代码平台必然只能采用「声明式」代码,这也是为什么所有低代码平台都会有内置的「DSL」
DSL 即「Domain Specific Language」,中文一般译为「领域特定语言」,在《领域特定语言》这本书中它有了一个定义:
一种为特定领域设计的,具有受限表达性的编程语言
界面渲染
核心原理是将 JSON 转成自研的 React/Vue 组件库,然后使用 React/Vue 进行渲染。
比如下面的JSON
代码语言:javascript复制{
"type": "page",
"title": "页面标题",
"subTitle": "副标题",
"body": {
"type": "form",
"title": "用户登录",
"body": [
{
"type": "input-text",
"name": "username",
"label": "用户名"
}
]
}
}
低代码原理就是转成了下面这样的 React/Vue 组件树,最终由各个 React/Vue 组件库渲染 HTML:
代码语言:javascript复制<Page title="页面标题" subTitle="副标题">
<Form title="用户登录">
<InputText name="username" label="用户名" />
Form>
Page>
交互逻辑的实现
交互及逻辑处理很难,目前常见有三种方案
- 图形化编程
- 固化交互行为
- 使用 JavaScript
- 图形化编程 图形化编程局限性很大,本质的原因是「代码无法可视化」。代码的抽象思维难以像积木一样进行拼接,积木拼接这种方式只适合用来实现简单的逻辑,比如 scratch。
- 固化交互行为 常用的交互行为固化并做成了配置,比如弹框的配置:
{
"label": "弹框",
"type": "button",
"actionType": "dialog",
"dialog": {
"title": "弹框",
"body": "这是个简单的弹框。"
}
}
请求成功后,显示反馈弹框
代码语言:javascript复制{
"type": "page",
"body": {
"type": "button",
"label": "ajax 反馈弹框",
"actionType": "ajax",
"api": "/api/mock2/form/saveForm",
"feedback": {
"title": "操作成功",
"body": "${id} 已操作成功"
}
}
- 但这个方案最大的缺点是灵活性受限,只能使用 自身开发的低码平台 内置的行为。
- 使用 JavaScript
- 要实现更灵活的控制,还是得支持JavaScript 其他语言工具中,比较常见的代表C Builder / Xcode,本质其实是一样的
<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="19162" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES" customObjectInstantitationMethod="direct">
<dependencies>
<deployment identifier="macosx"/>
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="19162"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
dependencies>
<objects>
<customObject id="-2" userLabel="File's Owner"/>
<customObject id="-1" userLabel="First Responder" customClass="FirstResponder"/>
<customObject id="-3" userLabel="Application" customClass="NSObject"/>
<customView id="c22-O7-iKe">
<rect key="frame" x="0.0" y="0.0" width="480" height="272"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<subviews>
<button verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="EWk-sd-qTp">
<rect key="frame" x="156" y="177" width="75" height="32"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" flexibleMinY="YES"/>
<buttonCell key="cell" type="push" title="Button" bezelStyle="rounded" alignment="center" borderStyle="border" imageScaling="proportionallyDown" inset="2" id="QXz-cE-Nde">
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
<font key="font" metaFont="system"/>
buttonCell>
button>
subviews>
<point key="canvasLocation" x="139" y="154"/>
customView>
objects>
document>
后端方案
如何实现自定义数据存储? 如何实现业务逻辑? 如何实现流程流转?