5、架构:通用 Schema 设计

news2024/11/25 4:59:49

作为前端开发一定会非常熟悉 AST 抽象语法树(Abstract Syntax Tree),当浏览器加载 JS 代码时,它会首先将代码转换为一棵抽象语法树(AST),然后再根据 AST 来渲染对应的 DOM 结构,对于一款低代码产品来说,如果能直接去解析 AST 肯定是最方便但这也是麻烦的,因为 AST 包含的内容非常多,所以大部分的低代码产品都会使用自定义的 Schema 来描述搭建的内容。

但也由于 Schema 只是一种通用的协议,并没有非常好的规范与最佳实践,现阶段都是属于各自为战的边界探索阶段,所以各个低代码平台中的 Schema 的规范并不相同。

其实就算不是探索阶段,大多数平台的低代码产品肯定也很难做到统一,除了开发者的习惯也会涉及到用户习惯以及行业差异、产品定位等,此外商业产品为了盈利会主动营造技术壁垒、增加用户粘性、培养用户习惯以及迁移成本。

但当我们想把这个产品升级为 Pro Code 或者想再添加更多交互功能的时候,是不是等同于又重新创建了一个新 DSL,这也是我个人感觉低代码一个非常尴尬的点

当然在产品的初期由于时间与资源有限,肯定不会最开始就设计 DSL 解析,所以接下来我们将围绕 Schema 来逐步分析从设计落地以及扩展的全过程。

什么是 Schema 协议


Schema 本质上就是一个 JSON 格式的定义块,通过抽象属性定义来表达页面和组件的布局、属性配置、依赖关系、表达式解析,如果在偏向业务也有页面路由、多语种、数据源、权限等等各种各样的抽象声明。 因此,我们也将刚刚提到的内容统称为 Schema 协议,它也属于元数据结构模型的范畴。

如果想要进行更多的了解,可以 Google 看看元数据的相关内容。或者入群探讨,小册里就不再过多展开。

什么是协议渲染


当了解了 Schema 的基本概念后,接下来就需要具体来实施和设计相关 Schema 协议的实现了。

在正式开始设计协议之前,我们一起先来看下面的例子,一起来了解下协议渲染究竟是什么东西?

相信很多朋友为了提高效率或多或少都封装过一些通用型的组件,比如通过 JSON 配置来实现一组表单布局,如下图,是一个简单的表单区块:

前端实现的代码如下所示:

<Form>
  <Form.Item
    label="用户名"
    name="username"
    >
    <Input />
  </Form.Item>

  <Form.Item
    label="性别"
    name="sex"
    >
    <Select />
  </Form.Item>

  <Form.Item wrapperCol={{ offset: 8, span: 16 }}>
    <Button htmlType="reset">
      重置
    </Button>
    <Button type="primary" htmlType="submit">
      提交
    </Button>
  </Form.Item>
</Form>

可以看出,这其中大部分的代码都是冗余,特别是对于中台场景或者问答场景中会频繁大量的出现,所以社区早期就出现了配置式的解法,根据相对应的配置规则,我们可以将其抽象为以下代码:

import { Form } from 'antd'
import { FormRender, FormRenderProps } from '@you-team/form-render'

const config: FormRenderProps['config'] = [
  // username form.item config.
  {
    label: '用户名',
    name: 'username',
    renderType: 'Input',
  },
  // sex form.item config.
  {
    label: '性别',
    name: 'sex',
    renderType: 'Select',
  }
]

<FormRender 
  as={Form} 
  config={config} 
  onFinish={data => console.log('FormData', data)} 
/>

上述方式就是一个简单表单类型的 Schema 设计,借助封装好的 FormRender 组件来递归约定好的表单 Schema 协议快速进行页面表单内容的渲染。

此类方案是协议约定式渲染的方案之一,在低代码平台中,通常也是使用相同的方式来实现的,只不过会更加的通用,复杂程度也会更高。

目前而言,社区存在很多类似的实现解法,大部分都是 UI(config) 的思想。如果感兴趣可以搜索下对应的文章学习与了解。

设计与实现


上述的例子非常简单也是大家常用的组件封装方式,接下来就是我们这个低代码产品的 Schema 协议 的设计与实现了,首先来说下整个协议的主体采取 JSON 方式的原因:

  • 方便存储,可以存储到服务端中形成记录;
  • 方便操作,跨平台解析;
  • 结构简单,通俗易懂,方便开发者查阅。

如下图所示,Schema 协议第一个版本先预留了如下几个领域区块,分别是依赖管理国际化(多语种)状态管理数据源生命周期以及页面结构等耳熟能详的结构定义。

协议版本(version)

代表着当前协议的版本,用于后续协议 break change 带来的兼容问题,可以通过版本来区分渲染器和解析器。而版本的升降级也是有规范可循的,如社区中比较常见的像semver,大体上的规则如下:

  • major: 如果包含 Break Change(破坏更新)的内容;
  • minor: 当你产出了一个新的功能的时候(无破坏更新);
  • patch: 当你修复了一个 BUG 问题的时候(无破坏更新)。

依赖管理(library)

代表当前协议在编辑器中依赖的一些类库和包,为后续异步加载资源和动态引入留坑位。在内部声明出依赖的名称、加载的资源地址(如组件库会导出 index.js 和 index.css 多个资源),类库的声明名称等等。那么可以分析得出如下依赖的大体结构:

const librarys: SchemaModelConfig['librarys'] = [
  {
    name: 'dayjs',
    urls: [
      'https://unpkg.com/dayjs@1.11.7/dayjs.min.js'
    ],
    globalVar: 'dayjs'
  },
  {
    name: 'arco',
    urls: [
      'https://unpkg.com/@arco-design/web-react@2.46.3/dist/arco.min.js',
      'https://unpkg.com/@arco-design/web-react@latest/dist/arco-icon.min.js',
      'https://unpkg.com/@arco-design/web-react@2.46.3/dist/css/arco.css',
    ],
    globalVar: 'Arco'
  }
]

国际化(i18n)

管理当前协议生成页面的 react-i18n-next 相关的键对值,用于维护国际化项目时需要进行多语种的文案切换带来的业务诉求。参考业内成熟 i18n 的方案,多语种的协议字段就相对而言比较简单:


  const i18n: SchemaModelConfig['i18n'] = {
    zh: {},
    eu: {},
    ...后续补充需要支持的多语种
  },

状态管理(store)

维护一份页面上的状态。方便后续做绑定通信和事件广播的实现,用于赋予整个页面的组件联动交互,最常见的就是点击相关按钮唤起相关弹窗类型组件

当低代码产物为工程类型时,那么就会涉及到跨模块跨页面这种全局状态管理,当然随之而来的是这块的配置会更加复杂,包括 Schema 的设计与配置的形式。

数据源(dataSource)

在大多数业务场景当中,页面的元素结构渲染并不是根据静态数据来渲染的,而是通过获取相关接口中的远程数据来展示。所以数据源与远端挂钩,可以是远程的 JSON 文件,也可以是一个 fetch 请求,主要的目的是为了帮助页面组件支持动态渲染数据的能力。

一个请求包含以下几个重要的内容,请求资源 URIRequestHeaderResponse => params | query | body ,所以在定义数据源的时候,我们将其抽象成如下结构:

const i18n: SchemaModelConfig['dataSource'] = [
  {
    key: 'string|uuid',
    name: 'getUserList',
    request: {
      url: 'https://localhost:3000/user/list',
      params: {
        pageSize: 10,
        current: 1,
      },
      method: 'GET',
      body: {},
      header: {}
      ...AxiosInstanceConfig
    }
  }
]

// 最终会抽象成一个函数调用来动态的执行。
lowcodeSandBox?.loadDataSource('getUserList', ...其他参数): Promise<any>

生命周期(lifeCycles)

一个项目的使用中有初始化使用中销毁等多个不同的生命周期,每个状态需要做的事情也不同,比如在程序初始化时会加载或者配置后续使用中需要的数据、资源等,同理对于低代码平台应用而言,搭建页面时与传统项目一样,同样需要自定义一套生命周期来帮助更好管理产物的拉取、Dom 渲染、数据更新等操作。

页面结构(htmlBody)

与 虚拟 DOM 相似,本质上是对于当前页面渲染的抽象结构,便于跨平台之间的转换,为后续运行时渲染和动态出码垫定基础,提供后续结构化转换的能力。

我们先来看一下 React 组件的createElement方法的构成:

React.createElement(type, props, children);
  • type: 可以是原生标签,也可以是 函数组件 和 Class 组件 等;
  • props:组件元素需要的属性;
  • chidren:组件内容;

熟悉 React 的同学都知道,在编译时我们所写的 JSX|TSX 会被编辑成 React.createElement 执行函数,而我们抽象出来的 Schema 结构也是做类似的事情。

如下代码所示,就是一个对页面的抽象设计,其中主要包含的内容就是渲染的组件名称Props子组件等等信息。

至于属性具体有什么作用,在后续相关的实战章节会着重的分析,在这里只要先了解页面结构的基本画像即可。

{
  "ROOT": {
    "type": {
      "resolvedName": "Container"
    },
    "props": {
      "width": 800,
      "height": "100%",
      "paddingTop": 20,
      "paddingBottom": 20,
      "paddingLeft": 20,
      "paddingRight": 20,
      "background": "#FFFFFF"
    },
    "displayName": "基础容器",
    "custom": {},
    "hidden": false,
    "nodes": [
      "rpVYvatknx"
    ],
    "linkedNodes": {}
  },
  "rpVYvatknx": {
    "type": {
      "resolvedName": "Text"
    },
    "props": {},
    "displayName": "文本",
    "custom": {},
    "parent": "ROOT",
    "hidden": false,
    "nodes": [],
    "linkedNodes": {}
  }
}

最后

以上就是根据思维导图中初步拟定的协议草稿的字段定义解释。

在有了明确的定义结构后,我们就可以写出一个简单的Schema的数据结构,如下所示:

const schema = JSON.stringify({
  version: 1.0.0,
  librarys: [],
  i18n: {
    zh: {},
    eu: {},
  },
  store: {},
  dataSource: {},
  lifeCycles: {},
  htmlBody: {
  "ROOT": {
    "type": {
      "resolvedName": "Container"
    },
    "props": {
      "width": 800,
      "height": "100%",
      "paddingTop": 20,
      "paddingBottom": 20,
      "paddingLeft": 20,
      "paddingRight": 20,
      "background": "#FFFFFF"
    },
    "displayName": "基础容器",
    "custom": {},
    "hidden": false,
    "nodes": [
      "rpVYvatknx"
    ],
    "linkedNodes": {}
  },
  "rpVYvatknx": {
    "type": {
      "resolvedName": "Text"
    },
    "props": {},
    "displayName": "文本",
    "custom": {},
    "parent": "ROOT",
    "hidden": false,
    "nodes": [],
    "linkedNodes": {}
  }
}
})

这里需要注意的是,你的协议一定要遵守 JSON 数据格式的约束,否则会导致解析时出现问题,为了避免开发中出现解析 Schema 产生不可预期的错误,可以使用第三方推荐的 JSON Schema 库来检验 Schema 是否符合规范:

  • json-schema-validator
  • json-schema

总结

作为低代码编辑器的通用能力之一,Schema 协议在设计到使用中起着至关重要的转换器作用,使得不同的编辑器和工具之间可以共享和使用相同的数据结构,方便地将数据在不同的应用程序和系统之间进行转换和交换,从而实现更高效、更可靠的工作流程。

目前初版协议起草其实已经能够面对绝大部分的问题了,在后续实战中涉及更加复杂的功能需要依赖协议的话,则可以在此基础上继续延伸做扩展。

此章节的内容会随着项目的更新进度不断优化

写在最后

如果你有什么疑问或者更好的建议,欢迎在评论区提出。 👏

5 架构:通用 Schema 设计

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/721275.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

接口自动化核心知识点浓缩,为面试加分

日常接触到的接口自动化从实际目标可以划分为两大类&#xff1a; 为模拟测试数据而开展的接口自动化 这种接口自动化大多是单次执行&#xff0c;目的很明确是为了功能测试创造测试数据&#xff0c;节约人工造数据的时间和人工成本&#xff0c;提高功能测试人员的测试效率。 …

Nuendo 12.0.70 资源下载及功能介绍

简介 Nuendo 12是一款屡获殊荣的影视、游戏和沉浸式环绕声音频后期制作软件&#xff0c;Nuendo 12在对白录音和编辑方面做了重大改进&#xff0c;为你的录音带来新声。Nuendo12把“对白”制作放到一个非常重要的位置&#xff0c;比以前更加贴近用户&#xff0c;它甚至起了一个…

读书笔记-《ON JAVA 中文版》-摘要19[第十八章 字符串-2]

文章目录 第十八章 字符串6. 常规表达式6.1 基础6.2 创建正则表达式6.3 量词6.4 CharSequence6.5 Pattern 和 Matcher6.5.1 find()6.5.2 组&#xff08;Groups&#xff09;6.5.3 start() 和 end()6.5.4 split()6.5.5 替换操作6.5.6 reset() 7. 扫描输入7.1 Scanner 分隔符7.2 用…

Python 解释器

文章目录 每日一句正能量前言交互式编程脚本式编程 每日一句正能量 不是因为生活太现实&#xff0c;而对生活失望&#xff1b;而是知道生活太现实&#xff0c;所以更要用心的活下去。给自己一个拥抱。 前言 Python 解释器是运行 Python 代码的工具&#xff0c;主要分为官方提供…

MySQL索引结构(面试题)、索引分类、语法

2索引 2.1 索引概述 2.1.1 介绍 ​ 索引&#xff08;index&#xff09;是帮助MySQL高效获取数据的数据结构(有序)。在数据之外&#xff0c;数据库系统还维护着满足 特定查找算法的数据结构&#xff0c;这些数据结构以某种方式引用&#xff08;指向&#xff09;数据&#xff…

【2022吴恩达机器学习课程视频翻译笔记】3.2线性回归模型-part-2

3.2线性回归模型-part-2 Let’s look in this video at the process of how supervised learning works. Supervised learning algorithm will input a dataset and then what exactly does it do and what does it output? Let’s find out in this video. Recall that a tr…

BeanUtils.copyProperties的11个坑

前言 我们日常开发中&#xff0c;经常涉及到DO、DTO、VO对象属性拷贝赋值&#xff0c;很容易想到org.springframework.beans.BeanUtils的copyProperties 。它会自动通过反射机制获取源对象和目标对象的属性&#xff0c;并将对应的属性值进行复制。可以减少手动编写属性复制代码…

高级Android开发人员枯竭,在这个利润丰厚的领域如何蓬勃发展

为什么高级人才供不应求&#xff1f; 技术行业的发展速度非常快&#xff0c;新的技术和工具不断涌现&#xff0c;导致技能需求不断演变。这使得不断更新和学习变得至关重要。行业发展速度超过了教育和培训体系的能力跟进。传统教育往往滞后于最新的技术趋势和实践&#xff0c;…

暑期数学建模赛事总结--小白快速入门数学建模

暑期数学建模赛事总结–小白快速入门数学建模 欢迎各位大神小白一起参加数学建模&#xff01;&#xff01;&#xff01; 这是我自己总结的一些关于数学建模的代码和资料&#xff0c;放在github上供大家参考&#xff1a;https://github.com/HuaandQi/Mathematical-modeling.git…

线程 线程池

大屏展示 10个线程同时查询统计各个表&#xff0c;并行处理&#xff0c; 主线程 把任务投递 给 线程池&#xff0c;线程池中调出一个线程去处理 希望以一种 轻量级的 方式 实现 "i" 线程安全 &#xff1a; compareAndSwap() : 针对某个变量实现 或 -- 确保线程安全 …

QSciintilla_gpl-2.10.8版本在QT6中编译问题解决方案汇总

1. QWheelEvent &#xff08;1&#xff09;event->delta()需修改为event->angleDelta().y()&#xff1b; 2. sprintf": 不是 "QString" 的成员 sprintf->asprintf 3. 无法打开QTextCodec .pro文件中添加 greaterThan(QT_MAJOR_VERSION, 5) { …

试运行llama-7B、vicuna-7b-delta-v1.1和vicuna-7b-v1.3

Chatgpt的出现给NLP领域带来了让人振奋的消息&#xff0c;可以很逼真的模拟人的对话&#xff0c;回答人们提出的问题&#xff0c;不过Chatgpt参数量&#xff0c;规模&#xff0c;训练代价都很昂贵。 幸运的是&#xff0c;出现了开源的一些相对小的模型&#xff0c;可以在本地或…

MySQL安装失败starting the sever

MySQL安装失败starting the sever 如果电脑是第一次安装MySQL&#xff0c;一般不会出现这样的报错。starting the sever失败&#xff0c;通常是因为上次安装该软件没有清除干净。 第一种解决方法&#xff1a;完全卸载mysql&#xff0c;重新安装 完全卸载该软件的办法&#…

Linux基础开发工具(yum、vim、gcc/g++、Makefile、gdb、git)

目录 一. yum yum list yum install yum remove 二. vim 命令模式 底行模式 多文件操作 批量注释 三. gcc/g 四. make/Makefile 五.gdb 六. git git clone git commit git push 一. yum 作为一款操作系统&#xff0c;免不了需要一些定制化的内容&#xff0c;这…

工地为什么要做能耗监测?

随着全球气候变化的影响日益严重&#xff0c;环保意识逐渐深入人心&#xff0c;绿色建筑和节能减排成为了建筑行业的热门话题。而在建筑工地中&#xff0c;能耗监测成为了一项不可或缺的任务。本文将从以下几个方面来探讨工地为什么要做能耗监测。 首先&#xff0c;能耗监测有助…

【新星计划·2023】Linux目录与文件之权限意义讲解

作者&#xff1a;Insist-- 个人主页&#xff1a;insist--个人主页 作者会持续更新网络知识和python基础知识&#xff0c;期待你的关注 前言 通过上篇文章&#xff0c;我们知道了Linux 系统内文件的三种身份(拥有者、群组与其他人)&#xff0c;还知道每种身份都有三种权限(rwx)…

【小梦C嘎嘎——启航篇】C++ 基础中的精华(一)

C 基础中的精华&#xff08;一&#xff09;&#x1f60e; 前言&#x1f64c;1、namespace&#xff08;命名空间&#xff09;的经典知识1.1 namespace 日常如何正确使用&#xff1f;1.2 cout和cin来源&#xff0c;以及为什么相对于C语言中的printf和scanf有自动识别数据类型的功…

记一次用arthas解决springboot项目引入ShardingSphere后启动过慢的问题

背景&#xff1a; springboot项目中由于需要加入了ShardingSphere&#xff0c;之后就发现项目启动时间由之前的十多秒&#xff0c;延长到了70秒左右&#xff0c;去除ShardingSphere之后又恢复十多秒。一直以为是ShardingSphere的问题&#xff0c;网上搜罗了各种信息&#xff0c…

华安联大 | 蓝牙人员定位与UWB人员定位技术的应用案例分析

引言&#xff1a;随着物联网和定位技术的快速发展&#xff0c;蓝牙人员定位和UWB人员定位成为了人员定位领域的两种重要技术&#xff0c;两者都能在安防、工业、矿山、救灾、海航等领域得到广泛的应用。 在这有用户或会问蓝牙人员定位和UWB人员定位谁优谁劣&#xff0c;那么这…

2023.7.5例题:HCIA初级实验-2

一、题目要求&#xff1a; 1.在AR1左侧使用DHCP协议使左边可自动获得ip,左侧为192.168.1.0网段ip 2.右侧使用手动配置ip---右侧为192.168.2.0网段ip 3.telent客户端可远程登录到AR1 4.笔记本可以访问DNS获取文件 5.全网可达 二、解题步骤 第一步&#xff1a; 在AR1左侧使…