
lowcode-engine 中 Modal 类组件 hidden 属性被强制置为 true 的排查与修复方案【免费下载链接】lowcode-engineAn enterprise-class low-code technology stack with scale-out design / 一套面向扩展设计的企业级低代码技术体系项目地址: https://gitcode.com/GitHub_Trending/lo/lowcode-engine导读在基于 lowcode-engine 搭建的低代码平台上Modal / Dialog 类弹窗物料常出现设计器里能看到、预览与渲染阶段却不渲染、拿不到实例的现象。本指南聚焦 FAQ 中记录的经典问题——Modal 类组件 hidden 属性被强制设置 true完整说明其成因、三种修复路径升级引擎、注册 Save 态 props transducer、以 Save 态导出 schema并结合本仓库源码逐层剖析 hidden 属性的写入机制、TransformStage 管道模型与addPropsTransducer/exportSchema的真实调用链帮助开发者在自己的引擎集成代码中定位并根治此类问题。一、问题现象与成因1.1 现象描述在设计器中拖入一个 Modal 类弹窗物料后设计器画布内正常展示切换到预览 / 渲染场景时Modal 组件不渲染通过this.$(xxx).open()等方式无法获取到组件实例打开弹窗的操作失效。1.2 根本原因根据 faq013.md 中的说明问题根源在于hidden属性设计器在画布中隐藏某个节点时会把hidden写入该节点的属性而hidden属性一旦被保留在导出/保存的 schema 中渲染端解析到hidden: true时便不再渲染该组件实例自然无法获取。该hidden并不是普通业务 props而是引擎的画布可见性标记。在 node.ts 中可以看到它的写入与读取逻辑setVisible(flag: boolean): void { this.getExtraProp(hidden)?.setValue(!flag); this.emitter.emit(visibleChange, flag); } getVisible(): boolean { return !this.getExtraProp(hidden)?.getValue(); }同时 node.ts 在节点初始化时会确保hidden扩展属性存在this.props.has(getConvertedExtraKey(hidden)) || this.props.add(false, getConvertedExtraKey(hidden));也就是说只要开发者在画布中点击工具栏的隐藏图标或调用setVisible(false)hidden: true就会进入该节点的 props并在 schema 导出/保存时被一并序列化出去最终导致 Modal 在渲染端不渲染。注意FAQ 首页在物料相关问题分类下同样收录了本条FAQ 概述。社区中也存在未主动隐藏却被置为 true的变体其共同解法都是围绕hidden在保存/导出阶段的处理展开。二、正确处理弹窗展示先明确设计器隐藏与运行态弹出是两件事2.1 弹窗的正确打开方式在动手改代码之前需要先确认一个前提设计器里的隐藏按钮与弹窗组件的open()能力是两个不同维度的操作。设计器隐藏控制该节点在画布上的可见性写入hidden扩展属性运行态打开依赖弹窗物料对外暴露的实例方法例如this.$(refId).open()。弹窗正确的展示/隐藏接入方式请参考 如何通过按钮展示/隐藏弹窗 中的完整步骤拖入按钮与弹窗 → 在弹窗高级面板中查看refId→ 隐藏画布中的弹窗 → 给按钮绑定事件通过代码打开this.$(pro-dialog-entryl32xgrus).open();该文档同时强调这种方式依赖低代码弹窗组件是否对外保留了相关 API不同物料支持的方式不一样。2.2 区分业务弹出控制与画布隐藏标记如果你的 Modal 物料本身不依赖hidden来控制弹出那么画布隐藏产生的hidden标记就属于纯设计态数据理应在 schema 落库/导出时被剥离这正是下文三个方案的核心思路。三、修复方案一推荐升级引擎版本FAQ 给出的首选方案是升级到 Engine Version 1.0.11 以上新版本中引擎对设计态隐藏标记与导出 schema 的处理进行了收敛hidden不再被无条件写入最终导出的 schema。这是改动最小、最不易引入回归的方案适用于可以自主升级引擎依赖的工程。适用前提与限制需要你当前使用的alilc/lowcode-engine版本低于 1.0.11升级前请先查看项目依赖中引擎与alilc/lowcode-types的版本配套关系本仓库的模块版本约束见 lerna.json避免类型枚举或 API 不兼容若因历史原因无法升级请使用下述方案二或方案三。四、修复方案二注册 Save 态 props transducer 剥离 hidden4.1 方案思路通过引擎提供的props transducer属性管道处理函数机制在 schema保存Save阶段对每个节点的 props 做一次过滤将hidden字段删除从而保证最终落库/导出的 schema 中不含该设计态标记。4.2 参考实现插件形态以下代码来自 faq013.mdimport { project } from alilc/lowcode-engine; import { IPublicEnumTransformStage } from alilc/lowcode-types; export const deleteHiddenTransducer (ctx: any) { return { name: deleteHiddenTransducer, async init() { project.addPropsTransducer((props: any): any { delete props.hidden; return props; }, IPublicEnumTransformStage.Save); }, }; } deleteHiddenTransducer.pluginName deleteHiddenTransducer;实现要点这是一个插件工厂函数返回插件对象pluginName必须显式赋值否则引擎注册插件时无法识别ctx为插件上下文此处未使用可按需保留签名project.addPropsTransducer(transducer, stage)注册一个针对指定 TransformStage 的 props 处理函数处理函数签名遵循 props-transducer.ts 定义的IPublicTypePropsTransducer类型export type IPublicTypePropsTransducer ( props: IPublicTypeCompositeObject, node: IPublicModelNode, ctx?: { stage: IPublicEnumTransformStage; }, ) IPublicTypeCompositeObject;即入参为当前节点的 props 对象与对应 shell 节点返回处理后的 props。这里直接delete props.hidden并在最后return props保证管道继续向后传递。4.3 底层原理propsReducers 管道与按阶段分发project.addPropsTransducer最终会把 transducer 注册到设计器的属性归约器集合中。调用链为Shell 层project.ts 中addPropsTransducer转发到designer.addPropsReducer(transducer, stage)设计器层designer.ts 中addPropsReducer将 reducer 按 stage 存入MapIPublicEnumTransformStage, IPublicTypePropsTransducer[]addPropsReducer(reducer: IPublicTypePropsTransducer, stage: IPublicEnumTransformStage) { if (!reducer) { logger.error(reducer is not available); return; } const reducers this.propsReducers.get(stage); if (reducers) { reducers.push(reducer); } else { this.propsReducers.set(stage, [reducer]); } }触发时机transformProps在导出 schema 时按 stage 取出对应 reducers 列表顺序执行、逐级传递transformProps(props, node, stage) { const reducers this.propsReducers.get(stage); if (!reducers) { return props; } return reducers.reduce((xprops, reducer) { try { return reducer(xprops, node.internalToShellNode() as any, { stage }); } catch (e) { console.warn(e); return xprops; } }, props); }两个值得注意的工程细节同一 stage 下按注册顺序执行若存在多个 transducer 需注意先后依赖单个 reducer 抛错不会中断整个管道引擎会console.warn后返回未处理的 props 继续执行避免一次异常导致导出失败但代价是该节点的这次处理被跳过因此自定义 transducer 内应避免抛错。4.4 TransformStage 各阶段语义transducer 只在指定的 stage 上生效这一点非常关键。引擎预定义的阶段枚举见 transform-stage.tsexport enum IPublicEnumTransformStage { Render render, Serilize serilize, Save save, Clone clone, Init init, Upgrade upgrade, }各阶段典型用途阶段值典型场景Renderrender渲染器渲染节点时获取 schemaSerilizeserilize序列化输出注意源码中的拼写即为此形式Savesave保存/落库前导出的 schemaCloneclone节点克隆时Initinit节点/文档初始化时Upgradeupgradeschema 升级时兼容说明类型包同时保留了一份标记为deprecated的TransformStage枚举值完全相同旧代码中使用TransformStage.Save依然可用新代码建议统一使用IPublicEnumTransformStage。按 Save 态注册 transducer 的语义是仅在保存/导出Save 阶段剥离 hidden渲染Render 阶段不受影响。若你希望同时在渲染阶段也不下发该标记可分别以IPublicEnumTransformStage.Render注册一份相同逻辑需自行评估是否会影响画布内已隐藏节点的显示行为。4.5 插件如何接入引擎将上述插件加入引擎的方式与普通插件一致例如在init阶段通过plugins.register注册或在引擎初始化配置中挂载插件列表。插件会在引擎启动后执行init完成 transducer 注册之后所有经 Save 阶段导出的 schema 都会自动剥离hidden。五、修复方案三导出 schema 时显式使用 Save 态如果不想以插件方式全局注册 transducer也可以只在导出 schema 的关键路径上显式指定 Save 态。FAQ 给出的代码为import { TransformStage } from alilc/lowcode-types; const schema project.exportSchema(TransformStage.Save)新写法使用推荐枚举二者等价import { IPublicEnumTransformStage } from alilc/lowcode-types; const schema project.exportSchema(IPublicEnumTransformStage.Save);5.1 exportSchema 的默认值与源码实现在 project.ts 中exportSchema(stage: IPublicEnumTransformStage IPublicEnumTransformStage.Render) { return this[projectSymbol].getSchema(stage); }⚠️关键坑点Shell 层exportSchema的默认参数是Render而设计器内部 project.ts 的getSchema默认参数是Save。如果不显式传参最终导出走的是Render 阶段你在 Save 阶段注册的删除 hidden transducer 将不会生效getSchema( stage: IPublicEnumTransformStage IPublicEnumTransformStage.Save, ): IPublicTypeProjectSchema { return { ...this.data, componentsMap: this.getComponentsMap(), componentsTree: this.documents .filter((doc) !doc.isBlank()) .map((doc) doc.export(stage) || {} as IPublicTypeRootSchema), i18n: this.i18n, }; }因此采用方案二 方案三组合时务必在导出调用处显式传入IPublicEnumTransformStage.Save二者阶段一致才能正确剥离 hidden。5.2 节点导出时 transducer 的触发位置exportSchema最终会走到文档与节点级导出node.ts 中每个节点的 props 与 extras 都会经过designer.transformProps(props, node, stage)const schema: any { ...baseSchema, props: this.document.designer.transformProps(props, this, stage), ...this.document.designer.transformProps(_extras_, this, stage), };这也解释了为什么delete props.hidden的写法足够因为hidden作为扩展属性会进入extras经getConvertedExtraKey归一或普通 props 集合而transformProps是对整个 props 对象做管道处理删除后不会再出现在导出结果中。若采用只保留白名单字段的更严格策略同样在此处生效。六、三种方案的选型建议与排查清单6.1 选型对照方案改动量生效范围适用场景升级到 1.0.11最小依赖升级引擎全局可以升级依赖的工程官方推荐Save 态 props transducer中等一个插件全局所有 Save 导出无法升级 / 需自定义更多 props 清洗逻辑导出时显式 Save 态小调用点改动仅显式调用点导出入口少、需要精确控制的场景三者并非互斥推荐升级版本 显式 Save 态导出组合若需保留对 hidden 的额外业务处理再叠加方案二的 transducer。6.2 复现排查清单在设计器中将弹窗节点隐藏导出 schema检查节点 props 中是否出现hidden: true确认预览端渲染逻辑是否把hidden当作不渲染信号部分 Modal 物料自身也用 hidden 控制显隐此时应走物料 API 而非删属性检查导出调用是否显式传了IPublicEnumTransformStage.Save避免落入默认Render阶段导致 transducer 不触发检查 transducer 是否在引擎启动后注册成功pluginName是否正确、init是否执行、有无被插件拦截验证最终 schemahidden已不在节点 props 中预览/渲染端 Modal 正常渲染且可通过this.$(refId).open()打开。七、总结Modal 类组件hidden被强制置为 true 的根因是设计器画布隐藏标记hidden 扩展属性被原样带入了运行态 schema。本仓库源码确认了该标记的写入setVisible/ 初始化兜底与导出链路getSchema → doc.export → node.transformProps → propsReducers按阶段管道执行的完整机制。实践上优先升级引擎版本或组合Save 态 props transducer 显式 Save 态导出双保险即可在不影响画布编辑体验的前提下让弹窗在预览与渲染阶段正常展示、实例可获取。相关可深入阅读的源码与文档还包括FAQ 总览docs/docs/faq/index.md弹窗按钮展示/隐藏教程docs/docs/demoUsage/makeStuff/dialog.md属性管道类型定义packages/types/src/shell/type/props-transducer.ts阶段枚举定义packages/types/src/shell/enum/transform-stage.ts设计器 reducer 注册与执行packages/designer/src/designer/designer.tsShell 层 project APIpackages/shell/src/api/project.ts设计器层 project 导出实现packages/designer/src/project/project.ts节点可见性与导出触发点packages/designer/src/document/node/node.ts【免费下载链接】lowcode-engineAn enterprise-class low-code technology stack with scale-out design / 一套面向扩展设计的企业级低代码技术体系项目地址: https://gitcode.com/GitHub_Trending/lo/lowcode-engine创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考