拓冰建站拓冰建站
首页 / 资讯中心 / 正文

lowcode-engine 渲染模块设计:从 Schema 与物料组件渲染页面的适配层、渲染层与多模式渲染实践

lowcode-engine 渲染模块设计从 Schema 与物料组件渲染页面的适配层、渲染层与多模式渲染实践【免费下载链接】lowcode-engineAn enterprise-class low-code technology stack with scale-out design / 一套面向扩展设计的企业级低代码技术体系项目地址: https://gitcode.com/GitHub_Trending/lo/lowcode-engine本篇技术指南围绕 lowcode-engine低代码引擎的渲染模块设计展开讲解基于 Schema 和物料组件如何渲染出页面这一核心问题从协议层、能力层、适配层、渲染层、应用层的五层架构出发深入到alilc/lowcode-renderer-core的adapter.setRuntime/adapter.setRenderers抹平 React 与 Rax 差异的实现再到react-renderer预览渲染与react-simulator-renderer设计态画布渲染的完整链路。读完后你将能够独立在项目中接入 React 渲染器完成 Schema 渲染、理解渲染器分发与容错机制的源码原理、并搞懂设计态 Simulator 如何作为设计器与渲染器之间的连接器工作。一、渲染要解决的问题低代码引擎搭建完成后会产出一份符合协议规范的 Schema页面/组件/区块的 JSON 描述同时存在一批物料组件如 Fusion 的Button、Table。渲染模块要回答的问题就是如何基于这份 Schema 和这些组件在运行时框架React、Rax 等上把页面真实地渲染出来。npm 包与仓库布局文档明确给出的包与代码位置如下三者共同构成渲染体系npm 包仓库目录职责alilc/lowcode-react-rendererpackages/react-rendererReact 框架下的渲染入口负责把 React API 注册进适配层alilc/lowcode-renderer-corepackages/renderer-core框架无关的核心渲染层适配层adapter、Base/Page/Component/Block 等渲染器—设计态渲染packages/react-simulator-renderer设计器画布的 React 渲染器实现BuiltinSimulatorRenderer核心代码入口为 packages/renderer-core/src/index.ts它统一导出了adapter、contextFactory、各渲染器工厂baseRendererFactory、pageRendererFactory、componentRendererFactory、blockRendererFactory、addonRendererFactory、tempRendererFactory、rendererFactory、types、utils与hoc。二、整体架构五层模型渲染体系采用五层架构原设计文档 docs/docs/guide/design/renderer.md 中的架构图协议层基于《低代码引擎搭建协议规范》见 docs/docs/specs/lowcode-spec.md产出的 Schema 作为规范协议。Schema 的类型定义在 packages/types 中如IPublicTypeRootSchema、IPublicTypeNodeSchema。能力层提供组件、区块、页面等渲染所需的核心能力包括 Props 解析、样式注入、条件渲染等。适配层由于运行时框架并不统一React / Rax / 小程序……统一使用适配层将不同框架的差异部分通过接口对外暴露让渲染层注册/适配所需方法保障渲染层与能力层通过适配层解耦连接、独立可扩展。渲染层提供核心渲染方法。不同运行时框架的渲染方法不同通过适配层注入只要提供适配层所需的接口即可实现渲染。应用层根据渲染层提供的方法应用到项目中按使用方式和规模可实现应用、页面、区块级别的渲染。下面结合源码重点解析适配层与渲染层这两层。三、适配层抹平框架差异适配层提供的是各框架之间的差异项。例如React.createElement与Rax.createElement的 API 形状不同需要在适配层做抹平。Adapter 的实现适配层的核心是 packages/renderer-core/src/adapter/index.ts 中的单例Adapter类。从源码结构看其关键设计有三点必需的运行时模块清单。builtinModules定义了适配层认为一个合法运行时必须具备的六个成员adapter/index.ts#L10builtinModules [Component, PureComponent, createElement, createContext, forwardRef, findDOMNode];注册时做严格校验。setRuntime在接收运行时前调用isValidRuntimeadapter/index.ts#L55-L73逐一检查上述模块是否存在缺失时会直接抛出runtime is invalid, module ${m} does not exist错误。这保证了渲染层可以无条件信任adapter.getRuntime()返回的对象。内置兜底空实现。构造函数中的initRuntime会为这六个成员预先安装空实现no-op class/函数使得 core 包在未注册任何框架时仍可被加载避免顶层 import 崩溃。除运行时外适配层还管理两类注册项setRenderers/getRenderers注册渲染器模块。类型定义IRendererModulespackages/renderer-core/src/types/index.ts#L262-L270包含BaseRenderer?、PageRenderer、ComponentRenderer、BlockRenderer?、AddonRenderer?、TempRenderer?、DivRenderer?。其中BaseRenderer是可选的自定义入口——baseRendererFactory()会优先返回外部注册的BaseRenderer用于整体替换默认基础渲染器。setConfigProvider/getConfigProvider注册 UI 库的全局配置组件如 Fusion 的ConfigProvider用于向整棵渲染树传递locale、device等环境信息。React / Rax 的注册方式在 React 环境下只需把 React API 注入适配层import { createElement } from react; import { adapter } from ali/lowcode-renderer-core; // 仓库内实际作用域为 alilc/lowcode-renderer-core adapter.setRuntime({ createElement, });Rax 环境完全对称只是换掉 import 来源import { createElement } from rax; import { adapter } from ali/lowcode-renderer-core; adapter.setRuntime({ createElement, });注册之后核心层rendererFactory、BaseRenderer等内部一律通过adapter.getRuntime()取createElement、Component、PureComponent等见 renderer.tsx#L12 与 base.tsx#L106从而自动适配当前框架渲染层代码本身与具体框架完全解耦。文档给出的运行时方法语义如下可直接对照IRuntime类型定义types/index.ts#L252-L260Component/PureComponent组件基类参考 React 同名类createContext创建 Context 对象React 渲染订阅该 Context 的组件时会从组件树中离自身最近的 Provider 读取 context 值createElement创建组件元素React 中即创建 React 元素forwardRefref 转发允许组件接收 ref 并向下转发给子组件findDOMNode访问底层 DOM 节点组件已挂载时返回对应原生 DOM 元素。四、渲染层从入口渲染器到 BaseRenderer入口渲染器rendererFactorypackages/renderer-core/src/renderer/renderer.tsx 中的rendererFactory()返回最终对外的Renderer类其职责是把 Schema 路由到正确的渲染器并做容错处理根节点合法性检查render中若schema.componentName ! Div !isFileSchema(schema)则报错模型结构异常即根组件必须是Page、Block、Component或Div以兼容乐高区块模板见 renderer.tsx#L144-L147。组件解析getComp()先从「适配层注册的渲染器 用户传入的 components」中按componentName查找找不到时回退到${componentName}Renderer如Page→PageRenderer。这也解释了为什么预览模式只需传入{ Button }而Page能自动命中PageRendererrenderer.tsx#L125-L136。容错组件内置FaultComponent捕获渲染异常后显示红框提示并输出控制台日志与NotFoundComponent组件未找到时的占位并支持通过faultComponent/faultComponentMap/notFoundComponentprops 覆盖。挂起控制shouldComponentUpdate直接返回!nextProps.suspendedrenderer.tsx#L93-L95。suspended置为true时最外层容器不再更新在下钻编辑或多引擎嵌套渲染场景中用于冻结画布。上下文注入最终渲染为AppContext.Provider → ConfigProvider → Comp三层嵌套把appHelper、components、engine通过 Context 下发给整棵树renderer.tsx#L159-L177。Renderer对外的完整 props 契约定义在IRendererPropstypes/index.ts#L96-L182常用项包括schema、components、locale/messages国际化、appHelper全局上下文this.utils等可访问项、componentsMap、designMode、suspended、onCompGetRef/onCompGetCtx实例获取钩子、customCreateElement自定义元素创建钩子、faultComponent、device、thisRequiredInJSE默认trueJSExpression 是否只允许通过this访问上下文、enableStrictNotFoundMode默认false开启后组件未找到时不再提供默认容器。BaseRenderer能力层的落地真正的渲染能力集中在 packages/renderer-core/src/renderer/base.tsx 的BaseRenderer约 1000 行它实现了文档所说的 Props 解析、生命周期、状态、数据源、国际化等核心能力。从源码结构看其初始化链路为__beforeInit → __init → __afterInitbase.tsx#L158-L181其中__init完成自定义方法绑定__bindCustomMethods与 i18n API 初始化__initI18nAPIs。主要能力生命周期桥接静态方法executeLifeCycleMethodbase.tsx#L41-L67从schema.lifeCycles中取出 JSExpression/JSFunction经parseExpression/parseThisRequiredExpression解析后在当前实例上apply执行getDerivedStateFromProps、componentDidMount、componentDidUpdate、getSnapshotBeforeUpdate都会转发到 Schema 声明的对应生命周期从而让纯 JSON 的 Schema 拥有类组件的完整生命周期语义。Schema 级 state 与数据源PageRenderer/CompRenderer/BlockRenderer在__afterInit中执行this.state this.__parseData(schema.state || {})与this.__initDataSource(props)并在componentDidMount中reloadDataSource数据源引擎来自alilc/lowcode-datasource-engine/interpret见 base.tsx#L5。表达式与 i18n__parseData/__parseProps/__parseExpression负责递归解析 Schema 中的 JSExpression、JSFunction、i18n 值__createVirtualDom、__createLoopVirtualDom负责把子节点 Schema 转为虚拟 DOM含循环、条件、JSSlot 等。可覆盖的扩展点__beforeInit、__afterInit被刻意留空base.tsx#L171-L181子类只需覆写即可插入自定义逻辑。Page / Component / Block 三种渲染器三种渲染器都是对BaseRenderer的薄封装差异只在命名空间与上下文注入PageRendererrenderer/page.tsx__namespace page注入pageContext支持schema.state在编排时被修改后通过componentDidUpdate里的 JSON 比对触发setState同步page.tsx#L24-L33若components中提供了Page组件则以该组件为外壳渲染。CompRendererrenderer/component.tsx__namespace component注入compContext支持props.noContainer解析后为真时不渲染自定义组件外壳仅渲染上下文与内容并会以 Schema 声明的自定义组件作为外壳。BlockRendererrenderer/block.tsx__namespace block用于区块渲染__checkSchema时额外允许Div。此外还注册了addonRendererFactory()AddonRenderer与tempRendererFactory()TempRenderer用于插槽/临时内容等场景。五、React Renderer把 React 注册进适配层由于渲染层内部技术栈统一是 React适配层 API 大多按 React 设计因此 React 侧的适配代码量很少——核心就是把 React API 注册到适配层。仓库中 packages/react-renderer/src/index.ts 的完整实现如下注意仓库内实际使用alilc作用域与设计文档中的ali作用域对应import React, { Component, PureComponent, createElement, createContext, forwardRef, ReactInstance, ContextType } from react; import ReactDOM from react-dom; import { adapter, pageRendererFactory, componentRendererFactory, blockRendererFactory, addonRendererFactory, tempRendererFactory, rendererFactory, types, } from alilc/lowcode-renderer-core; import ConfigProvider from alifd/next/lib/config-provider; window.React React; (window as any).ReactDom ReactDOM; adapter.setRuntime({ Component, PureComponent, createContext, createElement, forwardRef, findDOMNode: ReactDOM.findDOMNode, }); adapter.setRenderers({ PageRenderer: pageRendererFactory(), ComponentRenderer: componentRendererFactory(), BlockRenderer: blockRendererFactory(), AddonRenderer: addonRendererFactory(), TempRenderer: tempRendererFactory(), DivRenderer: blockRendererFactory(), }); adapter.setConfigProvider(ConfigProvider);文件末尾的factory()再包一层 React 特有的isValidComponent判断通过obj?.prototype?.isReactComponent || obj?.prototype instanceof Component识别 React 组件类react-renderer/src/index.ts#L60-L62并默认导出一个可直接ReactDOM.render(ReactRenderer .../)使用的类。两点值得注意把React/ReactDOM挂到window上是为了让 Schema 中的 JSFunction/JSExpression 在运行时能够引用全局 React 能力DivRenderer复用blockRendererFactory()与入口Renderer中根节点允许Div兼容乐高区块模板的设计相互对应。相关文档与示例可继续查看 packages/react-renderer/README.md 与 docs/docs/guide/create/useRenderer.md。六、Rax Renderer差异点在于重写方法Rax 的大多数 API 与 React 基本一致差异点在于部分方法需要重写如findDOMNode需借助rax-find-dom-node页面/组件/区块渲染器需要单独实现。Rax 侧的注册代码形如import { Component, PureComponent, createElement, createContext, forwardRef } from rax; import findDOMNode from rax-find-dom-node; import { adapter, addonRendererFactory, tempRendererFactory, rendererFactory, } from ali/lowcode-renderer-core; import pageRendererFactory from ./renderer/page; import componentRendererFactory from ./renderer/component; import blockRendererFactory from ./renderer/block; import CompFactory from ./hoc/compFactory; adapter.setRuntime({ Component, PureComponent, createContext, createElement, forwardRef, findDOMNode, }); adapter.setRenderers({ PageRenderer: pageRendererFactory(), ComponentRenderer: componentRendererFactory(), BlockRenderer: blockRendererFactory(), AddonRenderer: addonRendererFactory(), TempRenderer: tempRendererFactory(), });对比 React 版本可以看出适配层的价值core 层一行不改仅通过换运行时 换渲染器实现就完成了框架迁移。这也是Env枚举adapter/index.ts#L3-L5与isReact()判据存在的意义——core 内部可据环境做分支行为。七、多模式渲染7.1 预览模式渲染预览模式只需要 Schema 与 components 即可完成页面渲染是最小的接入形态import ReactRenderer from ali/lowcode-react-renderer; // 仓库内alilc/lowcode-react-renderer import ReactDOM from react-dom; import { Button } from alifd/next; const schema { componentName: Page, props: {}, children: [ { componentName: Button, props: { type: primary, style: { color: #2077ff, }, }, children: 确定, }, ], }; const components { Button, }; ReactDOM.render(( ReactRenderer schema{schema} components{components} / ), document.getElementById(root));这里componentName: Page会命中适配层注册的PageRenderer走getComp的${componentName}Renderer回退逻辑子节点Button则直接命中components中的 Fusion 组件。更完整的用法含appHelper、messages、locale等参数可参考 docs/docs/guide/create/useRenderer.md。7.2 设计模式渲染Simulator设计模式渲染就是将编排产出的《搭建协议》渲染成视图的过程。由于视图可交互必须处理好内部数据流、生命周期、事件绑定、国际化等。它也即画布渲染——画布是 UI 编排的核心融合了页面渲染以及组件/区块的拖拽、选择、快捷配置。画布渲染与预览渲染的本质区别在于画布渲染与设计器之间存在双向交互。为此新增了一层Simulator作为设计器和渲染的连接器它把设计器传入的DocumentModel和组件/库描述转成相应的 Schema 和组件类再调用 Render 层完成渲染。其职责链条Project → Document → Simulator → Node / Props / Prop / Settings / SettingField → 通用交互模型为Project顶层对象保留对所有文档模型的引用管理应用级 Schema 的导入与导出。Document文档模型包括 Simulator 与数据模型两部分Simulator 通过一份 Simulator Host 协议与数据模型层通信画布上的 UI 操作由此驱动数据模型变化。多文档设计 多 Tab 交互支持同时设计多个页面并在同一浏览器标签里完成搭建与应用属性配置。Simulator模拟器承载特定运行时环境的页面渲染及与模型层的通信。Node节点模型对可视化组件/区块的抽象保留 Props 的引用封装修改、编辑、保存、拖拽、复制等组件级 API。Props / Prop属性模型体系Prop 与某一具体属性一一映射提供属性变更的操作 API。Settings / SettingFieldSettingField是Setter属性设置器与Prop属性模型之间的连接也是实现多节点属性批处理的关键。通用交互模型内置拖拽、活跃追踪、悬停探测、剪贴板、滚动、快捷键绑定实现位于 packages/designer如dragon.ts、active-tracker.ts、detecting.ts、clipboard.ts与内置模拟器 packages/designer/src/builtin-simulator。设计器侧的模拟器创建入口为 packages/designer/src/builtin-simulator/create-simulator.ts。7.3 React 模拟器的源码印证packages/react-simulator-renderer/src/renderer.ts 是上述架构在 React 上的具体落地包含两个核心类DocumentInstancerenderer.ts#L33-L184每个文档一个实例。其schemagetter 通过this.document.export(IPublicEnumTransformStage.Render)实时从DocumentModel导出渲染态 Schema——这正是DocumentModel 转 Schema的实现点mountInstance/unmountInstance维护节点 ID 与 React 实例的映射并通过host.setInstance把实例上报给设计器侧的 Host供节点选中、下钻编辑使用。SimulatorRendererContainerrenderer.ts#L186-L538实现BuiltinSimulatorRenderer接口即画布容器。构造函数里通过host.connect/host.autorun订阅模型层变化designMode、locale、device、componentsMap、requestHandlersMap 等并基于host.project.documents用 mobx 响应式同步DocumentInstance列表内部用createMemoryHistory做文档间路由history.listen触发host.project.open(docId)这就是多文档多 Tab 的实现基础。run()方法最终把SimulatorRendererView渲染进#app容器并回调host.project.setRendererReady(this)renderer.ts#L491-L510。节点与实例的关联依赖两个 SymbolSYMBOL_VNID节点 ID与SYMBOL_VDID文档 ID挂载时打在实例上renderer.ts#L562-L563getClosestNodeInstance据此沿 DOM/fiber 向上查找最近的节点实例——这是画布上点击某个视觉元素能反查到 Node 模型的底层机制。此外createComponent方法把低代码组件 Schema 包装成可拖入画布的 React 组件内部复用预览模式的LowCodeRenderer即alilc/lowcode-react-renderer渲染并注入 mock 的_leaf以降低性能开销renderer.ts#L433-L489。7.4 模拟器的三个设计维度结合源码可以印证设计文档中模拟器介绍的三点思考运行时环境当前有 React 生态本仓库的 react-simulator-renderer / renderer-core与 Rax 生态后续可扩展 Vue、Angular 生态——适配层 Simulator 的注册式设计正是为注册不同运行时环境的渲染模块实现编辑器从 React 页面搭建到 Rax 页面搭建的迁移服务的。布局模式不同于 C 端营销页搭建中后台场景以表单、表格为主流式布局是主流选择面向设计师与产品也需要绝对布局方式进行页面研发。研发场景低代码搭建不仅有页面编排还有逻辑编排、业务编排等场景。基于以上思考lowcode-engine 通过基于沙箱隔离的模拟器技术实现了多运行时环境React、Rax、小程序、Vue、多模式流式布局、自由布局、多场景页面编排、关系图编排的 UI 编排。通过注册不同的运行时环境渲染模块可实现编辑器从 React 页面搭建到 Rax 页面搭建的迁移通过注册不同的模拟器画布可以基于 G6 或 mxgraph 做关系图编排也可以定制流式布局画布或自由布局画布。八、延伸阅读协议规范docs/docs/specs/lowcode-spec.md、docs/docs/specs/material-spec.md渲染器使用指南docs/docs/guide/create/useRenderer.md核心渲染层实现packages/renderer-core、packages/react-renderer设计态渲染实现packages/react-simulator-renderer设计器内置模拟器packages/designer/src/builtin-simulator设计文档原文docs/docs/guide/design/renderer.md【免费下载链接】lowcode-engineAn enterprise-class low-code technology stack with scale-out design / 一套面向扩展设计的企业级低代码技术体系项目地址: https://gitcode.com/GitHub_Trending/lo/lowcode-engine创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
分享:

看完干货,该让你的企业上线了

免费需求沟通 · 48 小时内出具建站方案 · 河南本地可上门