
lowcode-engine 节点模型 Node API 完全指南属性、方法与源码级原理剖析【免费下载链接】lowcode-engineAn enterprise-class low-code technology stack with scale-out design / 一套面向扩展设计的企业级低代码技术体系项目地址: https://gitcode.com/GitHub_Trending/lo/lowcode-engine节点模型Node是 lowcode-engine 设计器中最核心的数据结构它把搭建画布上的每一个组件实例抽象为一棵可编程的树节点承载着属性读写、Schema 导入导出、增删改排、选中悬停锁定等全部编辑能力。本文以官方文档 docs/docs/api/model/node.md 为骨架结合 packages/designer/src/document/node/node.ts、packages/types/src/shell/model/node.ts 与 packages/shell/src/model/node.ts 三份源码逐条讲解 Node 的属性与方法并深入到内部实现帮助你掌握节点模型的全部 API 与底层运行机制能直接在插件、命令或物料扩展中编写可靠的节点操作代码。基本介绍Node 模型在引擎中的定位节点模型是 lowcode-engine 文档树DocumentModel中「单个组件树节点」的抽象。在搭建协议中页面由一棵组件树构成树的根节点通常是Page向下层层嵌套容器与叶子节点Node 模型即对应这棵树上的每一个节点实例。从源码结构看Node 模型存在三层实现内部模型packages/designer/src/document/node/node.ts 中的Node类真正持有状态与逻辑的实现类基于alilc/lowcode-editor-core的obx/computed/action做响应式建模Shell 门面层packages/shell/src/model/node.ts 中的Node类面向外部插件/命令的只读门面内部通过nodeSymbol引用内部节点通过Node.create()完成门面实例的创建与缓存公共类型packages/types/src/shell/model/node.ts 中的IPublicModelNode接口对外公开的 API 契约即本文讲解的主体。IPublicModelNode继承自IBaseModelNode...泛型基类since标注为 v1.0.0。因此在实际开发中你拿到的node对象可以放心调用本文列出的全部属性与方法同时要留意部分 API 在不同小版本中的命名演进如isContainer→isContainerNode。节点的基本结构一个 Node 在协议层面对应 IPublicTypeNodeSchema搭建基础协议——单个组件树节点描述export interface IPublicTypeNodeSchema { id?: string; // 节点 id componentName: string; // 组件名称必填、首字母大写 props?: { children?: ... } IPublicTypePropsMap; // 组件属性对象 condition?: IPublicTypeCompositeValue; // 渲染条件 loop?: IPublicTypeCompositeValue; // 循环数据 loopArgs?: [string, string]; // 循环迭代对象、索引名称 [item, index] children?: IPublicTypeNodeData | IPublicTypeNodeData[]; // 子节点 isLocked?: boolean; // 是否锁定 conditionGroup?: string; title?: string; hidden?: boolean; // ... }内部Node构造时node.ts会从 schema 中解构出componentName / id / children / props与剩余字段extras普通属性进入Props实例extras则作为「扩展属性」保存随后调用initBuiltinProps()把hidden / title / isLocked / condition / conditionGroup / loop这组内置扩展属性预置好避免后续构造实例导致响应式 reaction 被多次执行。特殊节点类型内部模型中componentName存在若干约定取值见 node.ts 的注释特殊节点说明Page页面根节点Block区块节点Component组件/元件根节点Fragment碎片节点无 props有指令Leaf文字节点/表达式节点无 propsSlot插槽节点无 props有正常 children、slotArgs与指令其中Leaf节点在构造时node.ts会用children字段文本或 JSExpression直接初始化 props其余类型才会构建NodeChildren子节点集合并初始化父引用。属性详解身份与展示属性类型说明idstring节点 id。内部实现中由document.nextId(id)生成/归一化node.tstitlestring \| IPublicTypeI18nData \| ReactElement节点标题。优先取扩展属性title的字符串值否则回退到componentMeta.titlenode.ts。相关类型 IPublicTypeI18nDataiconIPublicTypeIconType图标直接透传componentMeta.icon。相关类型 IPublicTypeIconTypecomponentNamestring节点 componentNameindexnumber节点在父容器子节点列表中的下标无父节点时返回-1node.tszLevelnumber节点所在树的层级深度根节点深度为 0。实现为自父节点递归累加的 computed 值node.ts节点类型判定v1.1.0 起的新命名属性类型含义旧命名已废弃isContainerNodeboolean是否为「容器型」节点。实现为isParentalNode componentMeta.isContainernode.tsisContainerisRootNodeboolean是否为根节点即document.rootNode thisnode.tsisRootisEmptyNodeboolean是否为空节点无 children 或 children 为空即children ? children.isEmpty() : trueisEmptyisPageNodeboolean是否为 Page 节点即根节点且componentName Pagenode.tsisPageisComponentNodeboolean是否为 Component 节点即根节点且componentName ComponentisComponentisModalNodeboolean是否为「模态框」节点透传componentMeta.isModalisModalisSlotNodeboolean是否为插槽节点即_slotFor ! null componentName Slotnode.tsisSlotisParentalNodeboolean是否为父类/分支节点即!isLeafNodeisParentalisLeafNodeboolean是否为叶子节点即componentName Leaf文字/表达式节点isLeafisLockedboolean当前节点锁定状态v1.0.16实现为读取扩展属性isLocked的值node.ts—isRGLContainerNodeboolean是否为磁贴布局RGL容器节点v1.1.0。v1.0.16 - v1.1.0 使用isRGLContainer。内部以布尔状态_isRGLContainer存储node.tsisRGLContainervisibleboolean当前节点是否可见v1.1.0。内部实现为!getExtraProp(hidden)?.getValue()并提供set visible反向写入node.ts—注意isRGLContainerNode是可读写的属性用于将节点标记为磁贴布局容器通常配合画布上的自由布局/磁贴布局能力使用。树结构与关系属性类型说明documentIPublicModelDocumentModel \| null节点所属的文档模型。相关类型 IPublicModelDocumentModel详见 文档模型prevSiblingIPublicModelNode \| null前一个兄弟节点按index - 1从 children 中取node.tsnextSiblingIPublicModelNode \| null后一个兄弟节点按index 1取node.tsparentIPublicModelNode \| null父亲节点childrenIPublicModelNodeChildren \| null孩子节点模型相关类型 IPublicModelNodeChildren详见 节点子集slotsIPublicModelNode[]节点上挂载的插槽节点们slotForIPublicModelProp \| null当前节点为插槽节点时返回该插槽对应的属性实例相关类型 IPublicModelProp插槽机制在内部通过addSlot / unlinkSlot / removeSlot维护addSlot会先保证相同slotName的插槽唯一再建立父子关系并 push 到_slots数组node.ts。数据与设置属性类型说明propsIPublicModelProps \| null节点的属性集相关类型 IPublicModelProps详见 属性集合propsDataIPublicTypePropsMap \| IPublicTypePropsList \| null节点的属性集值序列化后的 props。相关类型 IPublicTypePropsMap、IPublicTypePropsListconditionGroupIPublicModelExclusiveGroup \| null条件组v1.1.0用于互斥条件展示相关类型 IPublicModelExclusiveGroupschemaIPublicTypeNodeSchema \| null符合搭建协议的节点 Schema 结构v1.1.0。内部为export(TransformStage.Save)且提供可写 setternode.ts相关类型 IPublicTypeNodeSchemasettingEntryIPublicModelSettingTopEntry对应的设置器顶层操作对象。内部通过document.designer.createSettingEntry([this])懒创建node.ts详见 设置器顶层操作对象其中propsData与props的区别值得注意props返回可操作的对象模型propsData返回纯数据内部通过props.export(TransformStage.Serilize).props计算且 Fragment 与叶子节点返回null见 node.ts。方法详解信息查询类getRect返回节点的尺寸、位置信息DOMRectgetRect(): DOMRect | null;内部实现根节点返回simulator.viewport.contentBounds其余节点通过simulator.computeRect(this)计算node.ts。在未挂载模拟器环境时可能返回null。hasSlots是否有挂载插槽节点hasSlots(): boolean; // 内部实现this._slots.length 0hasCondition是否设定了渲染条件hasCondition(): boolean;内部实现为读取扩展属性condition只要值存在且不等于与true即视为已设置node.ts。hasLoop是否设定了循环数据hasLoop(): boolean;判定规则node.tsloop值存在且为「非空数组」或「JSExpression 变量」时返回true。contains当前节点是否包含某子节点v1.1.0contains(node: IPublicModelNode): boolean;实现基于层级比对先判断node2的zLevel上溯到node1所在层级的祖先是否为node1node.ts。canPerformAction是否可执行某 actionv1.1.0canPerformAction(actionName: string): boolean;实现为从componentMeta.availableActions中过滤出满足condition的 action再判断名称是否命中node.ts。getDOMNode获取节点实例对应的 DOM 节点getDOMNode(): HTMLElement;内部通过模拟器获取组件实例再定位 DOMsimulator.getComponentInstances(this)?.[0]与simulator.findDOMNodes(instance)?.[0]node.ts。该方法在内部实现中被标记为 deprecated仅在画布已渲染时可用。getRGL获取磁贴相关信息getRGL(): { isContainerNode: boolean; isEmptyNode: boolean; isRGLContainerNode: boolean; isRGLNode: boolean; isRGL: boolean; rglNode: IPublicModelNode | null; }返回对象各字段含义node.tsisContainerNode/isEmptyNode当前节点是否为容器 / 是否为空isRGLContainerNode当前节点是否为磁贴容器isRGLNode父节点是否为磁贴容器isRGLisRGLContainerNode || (isRGLNode (!isContainerNode || !isEmptyNode))rglNode若当前是磁贴容器则返回自身否则在有磁贴关系时返回父节点否则为null。属性操作类getProp / getPropValue获取指定 path 的属性模型实例 / 实例值/** * 获取指定 path 的属性模型实例 * param path 属性路径支持 a / a.b / a.0 等格式 */ getProp(path: string, createIfNone: boolean): IPublicModelProp | null; getPropValue(path: string): any;内部实现为this.props.query(path, createIfNone)node.ts其中createIfNone默认为true——即缺省情况下查询不存在的属性会自动创建。相关类型 IPublicModelProp。getExtraProp / getExtraPropValue获取指定 path 的扩展属性模型实例 / 值。与普通属性的关键区别在于导出时扩展属性不挂在props之下而是与props同级例如condition、loop、isLocked等指令字段getExtraProp(path: string, createIfNone?: boolean): IPublicModelProp | null; getExtraPropValue(path: string): any;内部实现node.ts为this.props.get(getConvertedExtraKey(key), createIfNone)。getConvertedExtraKey位于 packages/designer/src/document/node/props/props.ts扩展属性在内部以___前缀EXTRA_KEY_PREFIX存储从而与普通属性区分而导出时Props.export()会把带前缀的键还原并提升到 schema 顶层这正对应文档中「与 props 同级」的行为。setPropValue / setExtraPropValue设置指定 path 的属性模型实例值setPropValue(path: string, value: IPublicTypeCompositeValue): void; setExtraPropValue(path: string, value: IPublicTypeCompositeValue): void;实现setPropValue通过getProp(path, true).setValue(value)完成setExtraPropValue通过getExtraProp(path).setValue(value)完成node.ts。相关类型 IPublicTypeCompositeValue复合值支持普通值、JSExpression、JSFunction、I18nData 等。实战建议对组件业务属性读写优先使用getProp / setPropValue对协议指令condition、loop、title、isLocked、hidden、conditionGroup等请使用getExtraProp / setExtraPropValue。Schema 导入导出类importSchema导入节点数据importSchema(data: IPublicTypeNodeSchema): void;内部调用this.import(data)对于父类节点会重新导入props与children对于叶子节点则把文本/表达式写入props.childrennode.ts。exportSchema导出节点数据exportSchema(stage: IPublicEnumTransformStage, options?: any): IPublicTypeNodeSchema;stage对应 IPublicEnumTransformStage共六个阶段阶段值说明Renderrender渲染阶段会额外输出docIdSerilizeserilize序列化阶段Savesave保存阶段默认schemagetter 即此阶段Cloneclone克隆阶段不输出idInitinit初始化阶段Upgradeupgrade升级阶段内部export()实现要点node.tsClone阶段不携带idRender阶段额外携带docId叶子节点直接导出children普通节点导出props经designer.transformProps按阶段转换与扩展属性到 schema 顶层父类节点且 children 非空时附带childrenoptions.bypassChildren可跳过子节点导出。树结构操作类insertBefore / insertAfter在指定位置之前/之后插入一个节点insertBefore( node: IPublicModelNode, ref?: IPublicModelNode | undefined, useMutator?: boolean, ): void; insertAfter( node: IPublicModelNode, ref?: IPublicModelNode | undefined, useMutator?: boolean, ): void;内部实现node.ts通过ensureNode把入参归一化为节点实例传入 schema 或裸对象会自动document.createNode再调用children.internalInsert(nodeInstance, refIndex, useMutator)。useMutator控制是否触发didDropIn / didDropOut等联动回调对应物料callbacks.onNodeAdd / onNodeRemove见 node.ts。replaceChild / replaceWith替换指定子节点 / 将当前节点替换成指定节点描述replaceChild(node: IPublicModelNode, data: any): IPublicModelNode | null; replaceWith(schema: IPublicTypeNodeSchema): any;replaceChild实现node.ts删除被替换节点的id用data创建新节点先insertBefore再remove旧节点若旧节点处于选中态则把选中转移到新节点。replaceWith则委托parent.replaceChild(this, schema)完成注意它复用当前节点的 id。mergeChildren执行新增、删除、排序等批量操作v1.1.0mergeChildren( remover: (node: IPublicModelNode, idx: number) boolean, adder: (children: IPublicModelNode[]) any, sorter: (firstNode: IPublicModelNode, secondNode: IPublicModelNode) number ): any;内部直接委托children.mergeChildren(remover, adder, sorter)node.ts三个回调分别负责「判定删除」「补充新子节点」「排序比较」是进行复杂子集批量编排的推荐入口。remove删除当前节点实例remove(): void;内部实现node.ts若存在父节点先发出node.remove.topLevel事件插槽节点走parent.removeSlot流程普通节点走parent.children.internalDelete。注意移除时useMutator、purge与suppressRemoveEvent均有默认值。交互状态类select / hoverselect(): void; // 选中当前节点实例 hover(flag: boolean): void; // 设置悬停态默认 flag trueselect实现为document.selection.select(this.id)node.tshover(true)调用designer.detecting.capture(this)hover(false)调用release(this)node.ts。相关选中区 selection、悬停探测 detecting。lock设置节点锁定状态v1.0.16lock(flag?: boolean): void; // 默认 flag true实现为setExtraProp(isLocked, flag)node.ts锁定后节点不可在画布与大纲树上被选中/悬停。isConditionalVisible / setConditionalVisible条件组可见性控制v1.1.0isConditionalVisible(): boolean | undefined; setConditionalVisible(): void;实现为对conditionGroup互斥条件组的查询与置位isConditionalVisible返回_conditionGroup?.isVisible(this)setConditionalVisible调用_conditionGroup?.setVisible(this)node.ts。源码级实现细节与测试印证1. 响应式建模内部Node使用obx.ref、obx.shallow、computed与action装饰器来自alilc/lowcode-editor-core管理状态_parent为obx.ref_slots与status为obx.shallowzLevel / title / index / componentMeta / propsData为computed。这意味着对节点属性的任何setValue都会自动驱动大纲树、设置面板等 UI 的响应式更新。2. 扩展属性的前缀存储如前面所述扩展属性键经getConvertedExtraKey处理后带___前缀存放props.tsgetOriginalExtraKey负责还原。这也是为什么getExtraProp系列 API 与getProp系列在读写路径上必须严格区分。3. 事件联动内部Node构造时注册了两个全局事件转发node.ts可见性变化转发NODE_VISIBLE_CHANGE子节点变化转发NODE_CHILDREN_CHANGE。属性变化则通过onPropChange订阅propChange事件node.ts。4. 测试印证仓库在 packages/designer/tests/document/node/ 下提供了丰富的用例可对照验证node.test.ts基础模型行为、node.add.test.ts插入/添加、node.modify.test.ts属性与 schema 修改、node.remove.test.ts删除/清理、node.dragdrop.test.ts拖拽落点联动、node-children.test.ts子集操作等是学习与验证 API 语义的最佳辅助材料。典型使用场景示例下面是一个在插件中综合使用 Node API 的示意以 Shell 层IPublicModelNode为准// 1. 读取与判定 const node document.getNode(nodeId); // 或从 selection 获取 if (node.isContainerNode !node.isEmptyNode) { console.log(容器节点子节点数, node.children.size); } console.log(层级深度, node.zLevel, 兄弟下标, node.index); // 2. 属性读写 node.setPropValue(title, 新的标题); const align node.getPropValue(align); // 普通属性 node.setPropValue(padding, { top: 8, bottom: 8 }); // 复合值 // 3. 扩展属性协议指令 if (!node.hasCondition()) { node.setExtraPropValue(condition, { type: JSExpression, value: this.state.visible, }); } if (node.hasLoop()) { node.setExtraPropValue(loopArgs, [item, index]); } // 4. 树结构操作 const newNode document.createNode({ componentName: Text, props: { children: hello }, }); node.insertAfter(newNode, node.children.get(0)); // 插入到第一个子节点之后 node.replaceWith({ componentName: Div, props: {} }); // 5. 选中、悬停、锁定 node.select(); node.hover(true); node.lock(true); // 等价于 setExtraPropValue(isLocked, true) // 6. Schema 导入导出 const schema node.exportSchema(save); console.log(schema.componentName, schema.props, schema.condition); node.importSchema({ componentName: Div, props: { style: { width: 100 } } }); // 7. 批量编排 node.mergeChildren( (child) child.getPropValue(hidden) true, // 删除隐藏的子节点 (children) children, // 不新增 (a, b) a.index - b.index, // 保持原序 );结语与关联文档Node 模型贯穿设计器的属性面板、大纲树、拖拽落点与 Schema 持久化全链路props / propsData对接 属性集合 与 属性children / slots对接 节点子集document对接 文档模型settingEntry对接 设置器顶层操作对象componentMeta对接 组件元数据select / hover则与 选中区、悬停探测 直接相关。理解 Node 的属性和方法是编写低代码引擎插件、物料回调与命令扩展的基础本文所引用的内部实现node.ts与门面层shell node.ts可作为进一步阅读的入口。【免费下载链接】lowcode-engineAn enterprise-class low-code technology stack with scale-out design / 一套面向扩展设计的企业级低代码技术体系项目地址: https://gitcode.com/GitHub_Trending/lo/lowcode-engine创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考