3 分钟带你快速了解阿里 lowcode-engine 低代码插件架构

前言

Low Code Engine 是阿里开源的低码引擎,这里不同于一般的代码引擎,耦合了业务属性。LowCode Engine 只是一个低码基座,提供编辑器二次开发的能力。

界面能力

基础能力介绍如下:

  • 顶部:操作区

    • 右侧:撤回和重做、保存到本地、重置页面、预览、异步加载资源
  • 左侧:面板与操作区

    • 大纲面板:可以调整页面内的组件树结构
    • 物料面板:可以查找组件,并在此拖动组件到编辑器画布中
    • 源码面板:可以编辑页面级别的 JavaScript 代码和 CSS 配置
    • Schema 编辑:可以编辑页面的底层数据
    • 中英文切换:可以切换编辑器的语言
  • 中部:可视化页面编辑画布区域

    • 点击组件在右侧面板中能够显示出对应组件的属性配置选项
    • 拖拽修改组件的排列顺序
    • 将组件拖拽到容器类型的组件中
    • 复制组件:点击组件右上角的复制按钮
    • 删除组件:点击组件右上角的 X 或者直接使用 Delete 键
  • 右侧:组件级别配置

    • 选中的组件:从页面开始一直到当前选中的组件位置,点击对应的名称可以切换到对应的组件上
  • 选中组件的配置:当前组件的大类目选项,根据组件类型不同,包含如下子类目:

    • 属性:组件的基础属性值设置
    • 样式:组件的样式
    • 事件:绑定组件对外暴露的事件
    • 高级:循环、条件渲染与 key 设置

架构

Low Code Engine 整体的架构梳理如下

编辑器

低码引擎只是一个空壳,是整个低码的引擎基座,可以在壳子上搭建出低码编辑器。引擎对外暴露了 ctx,提供了插件机制,能够注册以下能力:

Setters - 设置器

一个能够设置 String 格式的自定义 Setter 实现如下:

tsx 复制代码
export default class StringSetter extends React.PureComponent<StringSetterProps, any> {
  static displayName = 'StringSetter';

  render() {
    const { onChange, placeholder, value } = this.props;
    return (
      <Input
        size="small"
        value={value}
        placeholder={placeholder || ''}
        onChange={(val: any) => onChange(val)}
        style={{ width: '100%' }}
      />
    );
  }
}

可以使用到引擎提供的 onChangevalue props 完成与引擎之间的数据传递

官方提供了一些默认的 Setters 合集:@alilc/lowcode-engine-ext

注册官方预设 Setter 方式如下:

tsx 复制代码
import { IPublicModelPluginContext } from '@alilc/lowcode-types';
import AliLowCodeEngineExt from '@alilc/lowcode-engine-ext';

// 设置内置 setter 和事件绑定、插件绑定面板
const DefaultSettersRegistryPlugin = (ctx: IPublicModelPluginContext) => {
  return {
    async init() {
      const { setterMap, pluginMap } = AliLowCodeEngineExt;
      const { setters, skeleton } = ctx;
      // 注册 setterMap
      setters.registerSetter(setterMap);
    }
  }
}
export default DefaultSettersRegistryPlugin;

Plugins - 插件

lowcode-engine 提供的 plugin 机制,允许扩展面板

一个自定义面板插件,声明与注入方式如下:

tsx 复制代码
import { plugins } from '@alilc/lowcode-engine';
import { IPublicModelPluginContext } from '@alilc/lowcode-types';

const pluginA = (ctx: IPublicModelPluginContext, options: any) => {
  return {
    init() {
      // 往引擎增加面板
      ctx.skeleton.add({
        // area 配置见下方说明
        area: 'leftArea',
        // type 配置见下方说明
        type: 'PanelDock',
        content: <div>demo</div>,
      });
      ctx.logger.log('打个日志');
    },
    destroy() {
      console.log('我被销毁了~');
    },
  };
};

pluginA.pluginName = 'pluginA';

plugins.register(pluginA, { key: 'test' });

官方提供了一些默认的 Plugin 合集:@alilc/lowcode-engine-ext

Material - 物料

Lowcode-engine 支持加载物料集

物料集的好处是可以避免多次下载解析重复的 deps,提升物料加载解析速度

物料协议描述如下:

json 复制代码
{
  "packages": [
    {
      "package": "moment",
      "version": "2.24.0",
      "urls": [
        "https://g.alicdn.com/mylib/moment/2.24.0/min/moment.min.js"
      ],
      "library": "moment"
    },
    {
      "title": "fusion组件库",
      "package": "@alifd/next",
      "version": "1.26.4",
      "urls": [
        "https://g.alicdn.com/code/lib/alifd__next/1.26.4/next.min.css",
        "https://g.alicdn.com/code/lib/alifd__next/1.26.4/next-with-locales.min.js"
      ],
      "library": "Next"
    },
  ],
  "components": [
    {
      "exportName": "AlilcLowcodeMaterialsMeta",
      "npm": {
        "package": "@alilc/lowcode-materials"
      },
      "url": "https://alifd.alicdn.com/npm/@alilc/lowcode-materials@1.0.7/build/lowcode/meta.js",
      "urls": {
        "default": "https://alifd.alicdn.com/npm/@alilc/lowcode-materials@1.0.7/build/lowcode/meta.js",
        "design": "https://alifd.alicdn.com/npm/@alilc/lowcode-materials@1.0.7/build/lowcode/meta.design.js"
      }
    },
  ],
  "sort": {
    "groupList": [
      "精选组件",
      "原子组件",
      "低代码组件"
    ],
    "categoryList": [
      "基础元素",
      "布局容器类",
      "表格类",
      "表单详情类",
      "帮助类",
      "对话框类",
      "业务类",
      "通用",
      "引导",
      "信息输入",
      "信息展示",
      "信息反馈"
    ]
  },
  "groupList": [
    "精选组件",
    "原子组件",
    "低代码组件"
  ],
  "ignoreComponents": {}
}

以上引擎入口源码可以参考 packages/engine/src/engine-core.ts

Output - 出码

lowcode-engine 还提供了出码功能,支持直接产出搭建的页面的 procode

渲染器

一个最简单使用渲染器的 demo 如下:

tsx 复制代码
import ReactDOM from 'react-dom';
import React from 'react';

const SamplePreview = () => {
  return (
      <ReactRenderer
        schema={schema}
        components={components}
      />
  );
};

ReactDOM.render(<SamplePreview />, document.getElementById('ice-container'));

要做到渲染低码组件,需要 2 个关键参数

  • Schema

描述低码配置的 DSL

  • components

所依赖的组件 pkg CDN 地址,对应的 components 需要提前被加载

生产与消费流程

调试源码

参考文档,这里再总结一下,主要是以下几步:

  • 本地启动 lowcode-engine 项目

  • 启动 demo 或者脚手架创建出的 editor 站点

  • 开启浏览器代理插件

  • 可以通过断点调试,查看上下文信息

架构细节

engine-core

整体的设计架构图如下:

核心逻辑都在下方文件

packages/engine/src/engine-core.ts

tsx 复制代码
const innerWorkspace: IWorkspace = new InnerWorkspace(registryInnerPlugin, shellModelFactory);
const workspace: IPublicApiWorkspace = new Workspace(innerWorkspace);
const editor = new Editor();
globalContext.register(editor, Editor);
globalContext.register(editor, 'editor');
globalContext.register(innerWorkspace, 'workspace');

const innerSkeleton = new InnerSkeleton(editor);
editor.set('skeleton' as any, innerSkeleton);

const designer = new Designer({ editor, shellModelFactory });
editor.set('designer' as any, designer);

const { project: innerProject } = designer;

const innerHotkey = new InnerHotkey();
const hotkey = new Hotkey(innerHotkey);
const project = new Project(innerProject);
const skeleton = new Skeleton(innerSkeleton, 'any', false);
const innerSetters = new InnerSetters();
const setters = new Setters(innerSetters);

const material = new Material(editor);
const commonUI = new CommonUI(editor);
editor.set('project', project);
editor.set('setters' as any, setters);
editor.set('material', material);
editor.set('innerHotkey', innerHotkey);
const config = new Config(engineConfig);
const event = new Event(commonEvent, { prefix: 'common' });
const logger = new Logger({ level: 'warn', bizName: 'common' });
const common = new Common(editor, innerSkeleton);
const canvas = new Canvas(editor);
let plugins: Plugins;

const pluginContextApiAssembler: ILowCodePluginContextApiAssembler = {
  // eslint-disable-next-line @typescript-eslint/no-unused-vars
  assembleApis: (context: ILowCodePluginContextPrivate, pluginName: string, meta: IPublicTypePluginMeta) => {
    context.hotkey = hotkey;
    context.project = project;
    context.skeleton = new Skeleton(innerSkeleton, pluginName, false);
    context.setters = setters;
    context.material = material;
    const eventPrefix = meta?.eventPrefix || 'common';
    context.event = new Event(commonEvent, { prefix: eventPrefix });
    context.config = config;
    context.common = common;
    context.canvas = canvas;
    context.plugins = plugins;
    context.logger = new Logger({ level: 'warn', bizName: `plugin:${pluginName}` });
    context.workspace = workspace;
    context.commonUI = commonUI;
    context.registerLevel = IPublicEnumPluginRegisterLevel.Default;
    context.isPluginRegisteredInWorkspace = false;
    editor.set('pluginContext', context);
  },
};

const innerPlugins = new LowCodePluginManager(pluginContextApiAssembler);
plugins = new Plugins(innerPlugins).toProxy();
editor.set('innerPlugins' as any, innerPlugins);
editor.set('plugins' as any, plugins);

export {
  skeleton,
  plugins,
  project,
  setters,
  material,
  config,
  event,
  logger,
  hotkey,
  common,
  workspace,
  canvas,
  commonUI,
};

上面截取了一些逻辑,主要是创建了 Editor 实例,一切方法都挂载到 Editor 实例上,对外仅暴露 ctx,做了 pluginssettersmaterial 等 class 的初始化

在 core 完成初始化之后,可以通过 core 包 export 出去的 pluginsmaterial 等对象,进行 register 的调用,扩展插件、物料等。

如何调试

  1. 安装 XSwitch 工具 chromewebstore.google.com/detail/xswi...

  2. 启动 lowcode-engine 项目

sh 复制代码
npm install && npm run setup

默认 lowcode-engine 项目会启动在 5555 端口

  1. 通过脚手架创建编辑器项目

编辑器项目与 lowcode-engine 项目的区别为

  • 编辑器项目将所有的的 lowcode-engine 插件整合,通过插件的形式,拼凑出引擎的界面

参考文档

kotlin 复制代码
npm init @alilc/element your-element-name

脚手架选项,选择编辑器,完成项目创建

  1. 使用代理工具进行代理
js 复制代码
{  
    "proxy": [  
        [  
            "https://uipaas-assets.com/prod/npm/@alilc/lowcode-engine/(.*)/dist/js/engine-core.js",  
            "http://localhost:5555/js/AliLowCodeEngine.js"  
        ],  
        [  
            "https://uipaas-assets.com/prod/npm/@alilc/lowcode-engine/(.*)/dist/css/engine-core.css",  
            "http://localhost:5555/css/AliLowCodeEngine.css"  
        ],  
        [  
            "https?://uipaas-assets.com/prod/npm/@alilc/lowcode-engine/(.*)/dist/js/react-simulator-renderer.js",  
            "http://localhost:5555/js/ReactSimulatorRenderer.js"  
        ],  
        [  
            "https?://uipaas-assets.com/prod/npm/@alilc/lowcode-engine/(.*)/dist/css/react-simulator-renderer.css",  
            "http://localhost:5555/css/ReactSimulatorRenderer.css"  
        ]  
    ]  
}
相关推荐
qbbmnnnnnn3 分钟前
【CSS Tricks】如何做一个粒子效果的logo
前端·css
唐家小妹4 分钟前
【flex-grow】计算 flex弹性盒子的子元素的宽度大小
前端·javascript·css·html
涔溪6 分钟前
uni-app环境搭建
前端·uni-app
安冬的码畜日常10 分钟前
【CSS in Depth 2 精译_032】5.4 Grid 网格布局的显示网格与隐式网格(上)
前端·css·css3·html5·网格布局·grid布局·css网格布局
洛千陨10 分钟前
element-plus弹窗内分页表格保留勾选项
前端·javascript·vue.js
小小199212 分钟前
elementui 单元格添加样式的两种方法
前端·javascript·elementui
完球了31 分钟前
【Day02-JS+Vue+Ajax】
javascript·vue.js·笔记·学习·ajax
前端没钱32 分钟前
若依Nodejs后台、实现90%以上接口,附体验地址、源码、拓展特色功能
前端·javascript·vue.js·node.js
爱喝水的小鼠37 分钟前
AJAX(一)HTTP协议(请求响应报文),AJAX发送请求,请求问题处理
前端·http·ajax
dgiij38 分钟前
AutoX.js向后端传输二进制数据
android·javascript·websocket·node.js·自动化