详细介绍:阿里lowcode低码引擎源码(二)设计器渲染

之前有写一批关于低代码引擎代码大体框架的文章,看这篇之前可以先看看那篇,读不懂的地方可以评论我即使更新。

阿里lowcode低码引擎源码(一)解析基础流程_阿里低代码-CSDN博客

本期主要讲解设计器是如何注册资产包和渲染器renderer是如何渲染shema,这次代码过于庞大不适合像上期那样展示大量代码,本期主要讲解概念和实现方式

在进入渲染流程之前先了解一些对象在本轮渲染的作用

Editor对象

在渲染流程上转换、储存、提供资源低代码组件协议资源,注意是组件协议资源不是组件库本身,举个最简单的资产包

{
    "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"
      }
    },
    {
      "exportName": "AlifdLayoutMeta",
      "npm": {
        "package": "@alifd/layout",
        "version": "2.0.7"
      },
      "url": "https://alifd.alicdn.com/npm/@alifd/layout@2.0.7/build/lowcode/meta.js",
      "urls": {
        "default": "https://alifd.alicdn.com/npm/@alifd/layout@2.0.7/build/lowcode/meta.js"
      }
    },
    {
      "exportName": "AlifdFusionUiMeta",
      "npm": {
        "package": "@alifd/fusion-ui"
      },
      "url": "https://alifd.alicdn.com/npm/@alifd/fusion-ui@2.0.0/build/lowcode/meta.js",
      "urls": {
        "default": "https://alifd.alicdn.com/npm/@alifd/fusion-ui@2.0.0/build/lowcode/meta.js",
        "design": "https://alifd.alicdn.com/npm/@alifd/fusion-ui@2.0.0/build/lowcode/meta.design.js"
      }
    }
  ],
 。。。。
}

Editor下setAssets方法将components属性url加载后得到组件协议描述,然后再对asset进行保存

class Editor {
 。。。
 async setAssets(assets: IPublicTypeAssetsJson) {
    const { components } = assets;
    if (components && components.length) {
      const componentDescriptions: IPublicTypeComponentDescription[] = [];
      const remoteComponentDescriptions: IPublicTypeRemoteComponentDescription[] = [];
      components.forEach((component: any) => {
        if (!component) {
          return;
        }
        if (component.exportName && component.url) {
          remoteComponentDescriptions.push(component);
        } else {
          componentDescriptions.push(component);
        }
      });
      assets.components = componentDescriptions;
      assets.componentList = assets.componentList || [];
      // 如果有远程组件描述协议,则自动加载并补充到资产包中
      if (remoteComponentDescriptions && remoteComponentDescriptions.length) {
        await Promise.all(
          remoteComponentDescriptions.map(async (component: IPublicTypeRemoteComponentDescription) => {
            const { exportName, url, npm } = component;
            if (!url || !exportName) {
              return;
            }
            if (!AssetsCache[exportName] || !npm?.version || AssetsCache[exportName].npm?.version !== npm?.version) {
              await (new AssetLoader()).load(url);
            }
            AssetsCache[exportName] = component;
            function setAssetsComponent(component: any, extraNpmInfo: any = {}) {
              const components = component.components;
              assets.componentList = assets.componentList?.concat(component.componentList || []);
              if (Array.isArray(components)) {
                // 将远程url获取到的组件库加载到assets.components
                components.forEach(d => {
                  assets.components = assets.components.concat({
                    npm: {
                      ...npm,
                      ...extraNpmInfo,
                    },
                    ...d,
                  });
                });
                return;
              }
            }
            if ((window as any)[exportName]) {
              if (Array.isArray((window as any)[exportName])) {
                // setArrayAssets((window as any)[exportName] as any);
              } else {
                setAssetsComponent((window as any)[exportName] as any);
              }
            }
            return (window as any)[exportName];
          }),
        );
      }
    }
    // const innerAssets = assetsTransform(assets);
    this.context.set('assets', assets);
    this.notifyGot('assets');
  }
。。。
}
export function load(url: string, scriptType?: string) {
  const node = document.createElement('script');
  。。。
  node.src = url;
  // `async=false` is required to make sure all js resources execute sequentially.
  node.async = false;
  scriptType && (node.type = scriptType);
  document.head.appendChild(node);
  。。。。
}

Designer对象

利用协议包创建ComponentMeta,每一个组件对应一个 ComponentMeta 的实例其属性和方法就是描述协议中的所有字段,所有 ComponentMeta 都由设计器器的 designer 模块进行创建和管理

Area对象

Area对象这个在上期有调用记录,但本期还是粗略讲下

主要是设计器管理区域渲染的一个对象,例如leftArea,mainArea,rightArea,手下大概管理有这些东西

    WidgetContainer区域实际装载渲染内容的容器 (也就是最终视图渲染会调用其item)

    WidgetView小部件渲染组件壳子 不过其内容都由widget类型控制(PanelDock,Widget)通过      传入widget类型渲染其类型body

   Widget类型

   1.PanelDock->Panel 实现小部件渲染是渲染情况之一例如左侧区域图标,点击图片弹出内容这       种

  2.Widget实现小部件渲染是渲染情况之一例如中间设计器区域直接展示这种

BuiltinSimulatorHost 对象

这个SimulatorHost主要用于是设计器与renderer模块之间的通信,因为renderer是在iframe里面离开设计器单独渲染的不涉及与设计器相关交互所以增加一层host,host 可以访问到设计器中所有模块,并提供相关方法供renderer 层调用

如图所示,左侧就是我们的iframe里面的画布或者官方点叫renderer渲染器,右侧就是我们这个Host对象了,Host除了与我们这个renderer通讯外还可以和外界的设计器沟通达到设计器与renderer相关交互

Adapter对象(可以先不用管)

1.适配层提供的是各个框架之间的差异项。比如 React 和 Rax createElement 方法是不同的。所以需要在适配层对 API 进行抹平,我们当前只讨论react组件的渲染
情况,所以你基本看作适配器里面的方法就是原生React方法
2.除此之外适配器提供基本的页面渲染方法可以定制页面渲染的生命周期,定制导航,定制路由等,说人话就是写了一个基本的页面容器组件,就像一般组件他也是通过schema描述渲染,不过页面容器组件一般是在schema的最外层。(当然除了页面容易还有组件容器,区域容器不过本期暂时不涉及)

Document对象(DocumentModel)

DocumentModel

首先文档模型之前可以先说下渲染模型之间的所属关系
整体来看,一个 Project 包含若干个 DocumentModel 实例,每个 DocumentModel 包含一组
Node 构成一颗树(类似 DOM 树),每个 Node 通过 Props 实例管理所有 Prop。如下图

具体来说文档模型提供文档管理的能力,每一个页面即一个文档流,对应一个文档模型。文档模型包含了一组 Node 组成的一颗树,类似于 DOM。我们可以通过文档模型来操作 Node 树,来达到管理文档模型的能力。每一个文档模型对应多个 Node,但是根 Node 只有一个,即 rootNode 和 nodes。

了解完这些基本概率后下面就可以开始正式渲染流程了

首先设计器mainArea的渲染也就是中间画布区域(非renderer)的渲染

defaultPanelRegistry->DesignerPlugin->DesignerView->ProjectView->BuiltinSimulatorHostView -> Canvas -> Content 文件 渲染renderer器所在单独的iframe(这是源码的调用过程看不懂没关系知道就行就是通过defaultPanelRegistry插件最后渲染了content文件)

class Content extends Component<{ host: BuiltinSimulatorHost }> {
    state = {
        disabledEvents: false,
    };
    private dispose?: () => void;
    componentDidMount() {
        // const editor = this.props.host.designer.editor;
    }
    componentWillUnmount() {
        this.dispose?.();
    }
    render() {
        const sim = this.props.host;
        return (
            
posted @ 2026-01-30 13:32  clnchanpin  阅读(41)  评论(0)    收藏  举报