ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

lowcode-engine 属性集模型(Props Model)完全指南:IPublicModelProps 属性、方法与实战

lowcode-engine 属性集模型(Props Model)完全指南:IPublicModelProps 属性、方法与实战 lowcode-engine 属性集模型Props Model完全指南IPublicModelProps 属性、方法与实战【免费下载链接】lowcode-engineAn enterprise-class low-code technology stack with scale-out design / 一套面向扩展设计的企业级低代码技术体系项目地址: https://gitcode.com/GitHub_Trending/lo/lowcode-engine属性集Props是 lowcode-engine 文档模型中承载节点组件属性的核心数据结构。本篇指南以官方 API 文档 props.md 为骨架结合 Props 实现类 与 Prop 实现类 的源码系统讲解IPublicModelProps的全部属性与方法、路径寻址语法、普通属性与扩展属性extra prop的区别以及如何在插件与设置器中安全读写属性值。读完本文你将能够熟练使用属性集模型完成组件属性的增删改查、嵌套路径操作与 schema 导出。基本介绍什么是属性集模型在 lowcode-engine 中每个文档节点Node都对应一份组件配置而这份配置的主体就是属性集Props。属性集模型IPublicModelProps是节点props字段的面向 Shell 层的公开抽象定义于 packages/types/src/shell/model/props.ts其内部实现为 designer 包中的Props类props.ts。从类型定义看IPublicModelProps继承自IBaseModelPropsIPublicModelProp即它管理的是一个由若干IPublicModelProp单个属性模型组成的集合。与之配套的类型还包括IPublicModelProp单个属性的公开模型提供getValue/setValue/getAsString等方法IPublicModelNode属性集所属的节点模型。属性集模型于v1.0.0随 Shell 层模型体系一同发布since v1.0.0并在 v1.1.0 中补充了has与add两个方法since v1.1.0。从底层实现看Props类在内部维护一个items: IProp[]数组并通过obx.shallow与computed保持响应式能力——当某个属性的值发生变化时会通过Prop#emitChange触发GlobalEvent.Node.Prop.InnerChange事件并回调节点自身的emitPropChange见 prop.ts从而驱动设计器画布、属性面板的实时联动。属性PropertiesIPublicModelProps暴露了三个只读属性用于标识属性集自身的身份与归属。idtype {string}属性集实例的唯一标识。底层实现中由uniqueId(props)生成见 props.ts在文档生命周期内保持稳定可用于在调试或持久化场景中区分不同的属性集实例。pathtype {string[]}返回当前属性集的路径。对于节点根级属性集其path为空数组[]底层Props类中readonly path []对于嵌套属性如prop.get(...)返回的子级Prop其path由父级路径拼接当前 key 得到见 prop.ts。路径以数组形式表达便于程序化地追踪属性在 schema 中的位置。nodetype {IPublicModelNode | null}返回当前属性集所属的节点实例。Shell 层实现通过ShellNode.create(this[propsSymbol].getNode())将内部节点包装为公开的IPublicModelNode见 packages/shell/src/model/props.ts若底层节点不存在则返回null。这一属性在需要从属性集反查节点、进而操作节点本身如获取节点componentName、children时非常有用。路径寻址语法a / a.b / a.0属性集模型的所有方法都围绕**路径path**展开因此先理解路径语法是关键。底层Props#get实现见 props.ts对路径的解析规则如下单段路径如a直接在当前属性集中按 key 查找多段路径如a.b先取第一段a作为入口剩余b递归下钻数组下标如a.0当某段属性是列表类型list时数字段会被解析为数组下标——Prop#get中通过isValidArrayIndex(entry, this.size)校验并索引见 prop.ts。例如对 schema{ props: { title: 按钮, style: { color: red }, dataSource: [{ name: a }, { name: b }] } }getPropValue(title)、getPropValue(style.color)、getPropValue(dataSource.1.name)分别可命中普通属性、嵌套对象字段和列表元素。需要特别说明get的第二个参数createIfNone默认为false当目标属性不存在且createIfNone为true时会创建一个值为UNSET的占位Prop并写入集合见 props.ts若为false则返回null。setPropValue内部正是利用getProp(path, true)实现不存在即创建再写入的语义。方法MethodsgetProp获取指定 path 的属性模型实例。/** * 获取指定 path 的属性模型实例 * get prop by path * param path 属性路径支持 a / a.b / a.0 等格式 */ getProp(path: string): IPublicModelProp | null;返回类型为 IPublicModelProp。Shell 层通过ShellProp.create(...)包装内部 Prop见 packages/shell/src/model/props.ts。拿到属性实例后可继续调用其getValue()/setValue()/getAsString()等方法完成更精细的操作。注意getProp不会创建不存在的属性找不到时返回null。getPropValue获取指定 path 的属性模型实例值。/** * 获取指定 path 的属性模型实例值 * get value of prop by path * param path 属性路径支持 a / a.b / a.0 等格式 */ getPropValue(path: string): any;这是最常用的读值方法。Shell 层实现为this.getProp(path)?.getValue()见 packages/shell/src/model/props.ts底层getValue实际执行export(IPublicEnumTransformStage.Serilize)即返回序列化阶段的值见 prop.ts。返回值类型为any可能是字面量、{ type: JSExpression, value: state.x }表达式、对象、数组或 JSSlot 结构。getExtraProp获取指定 path 的属性模型实例。/** * 获取指定 path 的属性模型实例 * 注导出时不同于普通属性该属性并不挂载在 props 之下而是与 props 同级 * get extra prop by path * param path 属性路径支持 a / a.b / a.0 等格式 */ getExtraProp(path: string): IPublicModelProp | null;getExtraPropValue获取指定 path 的属性模型实例值。/** * 获取指定 path 的属性模型实例值 * 注导出时不同于普通属性该属性并不挂载在 props 之下而是与 props 同级 * get value of extra prop by path * param path 属性路径支持 a / a.b / a.0 等格式 */ getExtraPropValue(path: string): any;扩展属性extra prop是理解属性集的关键概念之一。它用于承载那些不属于组件 props 配置、但与节点生命周期/设计器行为强相关的元数据例如condition条件渲染、loop循环、hidden隐藏、isLocked锁定、title等。在Node构造函数中这些内置指令正是通过props.add(..., getConvertedExtraKey(condition))等方式写入属性集的见 node.ts。底层实现中扩展属性的 key 带有___前缀getConvertedExtraKey(key)将condition转换为___condition___而getOriginalExtraKey负责反向还原见 props.ts。Shell 层的getExtraProp即先转换 key 再查找见 packages/shell/src/model/props.ts。其导出时与 props 同级的语义体现在Props#export中导出时key 以___开头的属性会被剥离前缀并归入独立的extras对象而普通属性留在props对象内见 props.ts最终节点 schema 形如{ componentName: Button, props: { children: 确定 }, condition: true, loop: { type: JSExpression, value: state.list } }其中condition、loop与props同级。因此读写这类字段必须使用getExtraProp/getExtraPropValue/setExtraPropValue而不是普通属性方法。setPropValue设置指定 path 的属性模型实例值。/** * 设置指定 path 的属性模型实例值 * set value of prop by path * param path 属性路径支持 a / a.b / a.0 等格式 * param value 值 */ setPropValue(path: string, value: IPublicTypeCompositeValue): void;value的类型为 IPublicTypeCompositeValue即复合类型包含任意 JSON 值string/number/boolean/null/ 普通对象 / 数组IPublicTypeJSExpression如{ type: JSExpression, value: this.state.x }IPublicTypeJSFunction函数表达式IPublicTypeJSSlot插槽如{ type: JSSlot, value: [{ componentName: Text }] }由上述类型任意嵌套组成的复合数组/复合对象。底层Prop#setValue会根据值形态自动判定属性类型字符串/数字/布尔归类为literal数组为listJSSlot为slotJSExpression为expression普通对象为map见 prop.ts。设置完成后会触发setupItems重建子属性并派发变更事件保证响应式链路不中断。实测示例见 props.test.tsprops.setPropValue(a, 2)后getPropValue(a)返回2。setExtraPropValue设置指定 path 的属性模型实例值扩展属性版。/** * 设置指定 path 的属性模型实例值 * set value of extra prop by path * param path 属性路径支持 a / a.b / a.0 等格式 * param value 值 */ setExtraPropValue(path: string, value: IPublicTypeCompositeValue): void;Shell 层实现先经getConvertedExtraKey转换 key再调用内部setValue见 packages/shell/src/model/props.ts。典型用途如设置节点的condition条件渲染开关、loop循环数据源等指令级属性。与setPropValue相同value同样支持 IPublicTypeCompositeValue 复合类型。has当前 props 是否包含某 prop。/** * 当前 props 是否包含某 prop * check if the specified key is existing or not. * param key * since v1.1.0 */ has(key: string): boolean;since v1.1.0。底层实现直接查询内部maps集合见 props.ts时间复杂度 O(1)。注意has只判断 key 是否存在不校验值是否为UNSET未设置占位。在Node初始化指令时正是用props.has(getConvertedExtraKey(condition))判断是否需要补默认值见 node.ts。add添加一个 prop。/** * 添加一个 prop * add a key with given value * param value * param key * since v1.1.0 */ add(value: IPublicTypeCompositeValue, key?: string | number | undefined): any;since v1.1.0。key可选传入时以指定 key 新增属性不传则由实现决定如列表场景按序号。底层Props#add会创建一个新Prop并追加到items返回该 Prop 实例见 props.ts因此可链式调用返回值的setValue等方法。value同样为复合类型。实战在插件/设置器中读写属性将上述 API 组合起来即可在插件或自定义设置器中完成常见的属性操作。以下示例基于documentModel与节点模型// 获取当前选中节点 const node documentModel.getNode(xxx) || documentModel.selection.getNodes()[0]; const props node.props; // IPublicModelProps // 1. 读取普通属性与嵌套路径 const title props.getPropValue(title); const color props.getPropValue(style.color); // 2. 写入属性不存在则自动创建 props.setPropValue(title, 新标题); props.setPropValue(style.color, #ff6600); // 3. 读取表达式属性复合值 const expr props.getPropValue(visible); // expr { type: JSExpression, value: state.visible } // 4. 操作扩展属性与 props 同级导出的指令字段 const cond props.getExtraPropValue(condition); // true / false / 表达式 props.setExtraPropValue(condition, false); // 让组件在渲染阶段不展示 props.setExtraPropValue(loop, { type: JSExpression, value: state.list }); // 5. 判断与新增 if (!props.has(dataSource)) { props.add([], dataSource); // 新增一个空数组属性 } // 6. 通过 getProp 拿到实例做精细操作 const prop props.getProp(title); if (prop) { const s prop.getAsString(); // 字面量转字符串 prop.setValue(又一个标题); }几点实战建议优先使用getPropValue/setPropValue而非先getProp再取值前者写法更简洁且自带安全防护在设置器Setter内部通常通过SettingTarget操作属性其底层同样委托到本文所述属性集模型见 prop.ts 中的getPropValue/setPropValue/clearPropValue注释see SettingTarget修改属性后无需手动刷新画布变更事件GlobalEvent.Node.Prop.InnerChange会自动驱动相关视图更新若要移除某属性可先getProp(path)拿到实例再调用其unset()标记为未设置或remove()从父级移除见 prop.ts 与 prop.ts。与节点模型的关系及内部机制属性集不是孤立存在的它与节点模型深度绑定初始化Node构造函数中通过new Props(this, props, extras)创建属性集其中props与extras来自 schema 解构const { componentName, id, children, props, ...extras } nodeSchema见 node.ts这正是扩展属性与 props 同级在导入方向的体现查询转发节点上的getProp/getPropValue/setPropValue/setExtraPropValue等方法均转发给this.props见 node.ts因此既可通过node.props也可直接通过节点方法操作属性导出序列化Props#export(stage)依据map/list两种存储形态分别导出为props对象或props数组并将___前缀的扩展属性剥离到extras见 props.tsProp#export则按literal / expression / slot / map / list五种类型递归导出见 prop.ts其中JSSlot在Render阶段还会附加params、id等运行时信息响应式与变更属性值变更经由emitChange抛出Node.Prop.InnerChange事件并回调owner.emitPropChange见 prop.ts同时Props#import/merge支持整体替换与增量合并配合action保证事务一致性。测试佐证属性集的行为在仓库中有完整测试覆盖props.test.ts 验证了getNode、get、getPropValue、setPropValue、嵌套路径z.z1、自动创建属性get(l, true)等核心能力prop.test.ts 则覆盖单个Prop的setValue、类型判定、unset、remove、export等行为。阅读测试用例是理解属性集边界行为如UNSET占位、createIfNone语义、列表下标合法性校验的最佳途径。小结属性集模型IPublicModelProps是 lowcode-engine 中连接 schema、文档节点与 UI 面板的枢纽通过a / a.b / a.0的统一路径语法配合getProp/getPropValue/setPropValue与扩展属性系列方法开发者可以精确、安全地操作任意节点的组件配置理解其底层Props/Prop实现、___前缀的扩展属性机制与复合值类型系统则能帮助你在插件开发、自定义 Setter、schema 迁移等场景中写出更可靠的低代码扩展代码。【免费下载链接】lowcode-engineAn enterprise-class low-code technology stack with scale-out design / 一套面向扩展设计的企业级低代码技术体系项目地址: https://gitcode.com/GitHub_Trending/lo/lowcode-engine创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进