- 前端
- UI组件
【免费下载链接】formily
📱🚀 🧩 Cross Device & High Performance Normal Form/Dynamic(JSON Schema) Form/Form Builder -- Support React/React Native/Vue 2/Vue 3
本文深入解析 Formily 核心包(
packages/core)中的ObjectField对象字段模型。ObjectField 用于承载值为对象类型的字段节点,它继承了 Field 模型的全部能力,并扩展出addProperty、removeProperty、existProperty三个用于动态管理对象属性的方法。读完本文,你将掌握如何创建 ObjectField、如何安全地增删对象属性并触发响应式更新、ObjectField 的自动清理机制,以及IObjectFieldState状态类型的完整含义,可直接用于 Formily 驱动的复杂嵌套表单开发。
一、ObjectField 是什么
ObjectField 是调用表单模型的createObjectField工厂方法所返回的字段模型实例。在 Formily 的字段体系里,共有四类字段模型:
| 模型 | 数据形态 | 说明 |
|---|---|---|
Field | 任意标量 | 基础字段,其他字段的基类 |
ArrayField | 数组 | 数组型字段(GeneralField联合类型成员) |
ObjectField | 对象 | 对象型字段(GeneralField联合类型成员) |
VoidField | 无数据 | 纯结构型字段(GeneralField联合类型成员) |
因为 ObjectField 是继承自 Field 模型 的,所以它天然具备 Field 的全部属性、方法与类型体系(标题、数据源、校验器、展示状态、交互模式、生命周期等)。本文只讲解 ObjectField 在 Field 之上扩展出的能力,其余 API 请参考 Field 文档。
1.1 创建 ObjectField
通过表单实例的工厂方法创建:
interface createObjectField { (props: IFieldFactoryProps): ObjectField }在 Form 模型的工厂实现 中可以看到,createObjectField内部会将basePath与name拼接成节点地址,并对 value 做了强约束:
new ObjectField( address, { ...props, value: isObj(props.value) ? props.value : {}, }, this, this.props.designable )也就是说:即使你不传 value,ObjectField 的 value 也会被规范化为空对象{},这一点与 ArrayField 强制为数组[]的行为一致,是"字段值类型由字段种类决定"这一设计的具体体现(Field 文档中的FieldValue类型也明确指出:"如果在 ArrayField 中是强制数组类型,ObjectField 中是强制对象类型")。
工厂方法同样遵循"路径复用"约定:如果相同路径已存在字段实例,多次调用会复用该实例,不会重复创建。
1.2 类型判定工具
从源码的 类型检查器 可以看到,Formily 还提供了配套的类型守卫:
isObjectField(node):判断节点是否为 ObjectField 实例;isObjectFieldState(state):通过state?.displayName === 'ObjectField'判断状态对象是否为对象字段状态。
对应测试可见 externals.spec.ts,它们常被用于表单图(Form Graph)的遍历与状态还原场景。
二、ObjectField 的扩展方法
ObjectField 在 Field 之上只扩展了三个方法,全部围绕"对象属性的动态管理"展开。这三个方法的实现集中在 ObjectField 模型源码:
2.1 addProperty —— 添加对象属性
描述:给对象添加属性,并触发onInput。
签名:
interface addProperty { (key: FormPathPattern, value: any): Promise<void> }实现原理:
addProperty = (key: string, value: any) => { this.form.setValuesIn(this.path.concat(key), value) this.additionalProperties.push(key) return this.onInput(this.value) }源码中addProperty并非简单地在本地对象上赋值,而是委托给表单层的setValuesIn按路径精确写入值,随后把新增的 key 记录进内部的additionalProperties数组,最后调用onInput触发输入事件——这正是文档所说"触发 onInput"的底层来源。由于写入与触发都发生在响应式环境中,UI 会随之自动更新。
2.2 removeProperty —— 移除对象属性
描述:移除对象属性,并触发onInput。
签名:
interface removeProperty { (key: FormPathPattern): Promise<void> }实现原理:
removeProperty = (key: string) => { this.form.deleteValuesIn(this.path.concat(key)) this.additionalProperties.splice(this.additionalProperties.indexOf(key), 1) return this.onInput(this.value) }removeProperty对应调用表单层的deleteValuesIn删除路径上的值,同时从additionalProperties中同步移除记录,最后同样触发onInput。
2.3 existProperty —— 判断属性是否存在
描述:判断属性是否存在。
签名:
interface existProperty { (key: FormPathPattern): boolean }实现原理:
existProperty = (key: string) => { return this.form.existValuesIn(this.path.concat(key)) }该方法是纯查询操作,通过existValuesIn判断当前字段路径下是否存在指定 key 对应的值,不会触发任何副作用。
2.4 测试用例印证
在 object.spec.ts 中,三个方法的行为被完整验证:
const object = attach( form.createObjectField({ name: 'object', value: {}, }) ) expect(object.value).toEqual({}) object.addProperty('aaa', 123) expect(object.value).toEqual({ aaa: 123 }) // 添加属性生效 object.removeProperty('aaa') expect(object.value).toEqual({}) // 移除属性生效 expect(object.existProperty('aaa')).toBeFalsy() // 属性已不存在三、ObjectField 的自动清理机制(makeAutoCleanable)
这是 ObjectField 区别于普通字段的关键机制,也是源码中除三个公开方法之外最重要的部分。在 ObjectField 构造函数 中,创建实例时会调用makeAutoCleanable():
protected makeAutoCleanable() { this.disposers.push( reaction( () => Object.keys(this.value || {}), (newKeys) => { const filterKeys = this.additionalProperties.filter( (key) => !newKeys.includes(key) ) cleanupObjectChildren(this, filterKeys) } ) ) }其工作流程如下:
- 通过
@formily/reactive的reaction订阅Object.keys(this.value)——即当前对象字段的所有键; - 当对象键集合发生变化时,对比
additionalProperties(通过addProperty添加的键),找出那些已被移除但尚未清理的键; - 调用 cleanupObjectChildren 清理函数,遍历表单字段图,把路径前缀匹配当前对象字段地址、且首段 key 命中的子字段统一
destroy()掉。
意义:当你通过removeProperty移除一个属性时,与之绑定的子字段模型(及其 UI、校验状态、副作用)也会被一并销毁,避免出现"数据已删除但字段节点残留"的内存泄漏与状态脏数据。这一点在 field.spec.ts 的 auto clean with ObjectField 用例 中有直接覆盖:先创建对象字段及其子字段aa、bb,随后通过移除属性验证对应子字段模型被自动销毁。
四、状态类型 IObjectFieldState
ObjectField 的状态类型为IObjectFieldState,其主要属性参考 IFieldState,唯一差别在于:value的数据类型要求是对象。
IFieldState 中与 value 强相关的核心字段包括:
| 字段 | 说明 |
|---|---|
value | 字段值(ObjectField 下强制为对象) |
initialValue | 字段默认值 |
inputValue/inputValues | 触发 onInput 收集到的值(集合) |
display/pattern | 展示形态与交互模式 |
required/validator | 必填标记与校验器 |
valid/invalid/validateStatus | 校验状态 |
errors/warnings/successes | 汇总(含子节点)反馈消息 |
在操作对象字段时,如果希望通过表单层的setFieldState/getFieldState批量读写状态,可直接将IObjectFieldState作为 setter 的类型依据;配合getFormGraph/setFormGraph还能对整棵字段树做序列化与还原。另外 Formily 的类型约定是:简单对象类型统一以I开头使用interface定义,IObjectFieldState正是遵循这一约定。
五、ObjectField 的典型应用场景
综合以上机制,ObjectField 最常见的落地场景包括:
- 嵌套对象数据结构的表单:如用户信息(姓名、联系方式、地址子对象)、配置面板中的分组数据,用 ObjectField 承载整个对象,用子 Field 表达具体属性;
- 动态增删键值对:结合
addProperty/removeProperty实现"动态添加/删除某个对象成员"的功能,而无需手动维护字段树,自动清理机制会保证字段节点与数据保持一致; - 子表单(嵌套表单):ObjectField 继承自 Field 的
submit、validate、reset等能力天然支持"包含所有子节点"的批量操作(Field 文档明确指出这些 API 主要用于子表单场景),配合对象形态的数据组织即可搭建多层嵌套表单。
总结
ObjectField 是 Formily 表单模型体系中管理"对象型数据"的核心节点:它复用 Field 的全部基础能力,仅以三个方法(addProperty、removeProperty、existProperty)完成对象属性的动态增删与查询;其底层委托表单层的路径化读写 API 并触发onInput,保证响应式更新;makeAutoCleanable自动清理机制则确保被移除属性对应的子字段模型不会残留。理解 ObjectField 的实现与状态类型,是掌握 Formily 嵌套表单与动态表单开发的关键一步。更多细节可继续阅读 Field 模型文档、Form 模型文档 以及 ObjectField 源码。
- 前端
- UI组件
【免费下载链接】formily
📱🚀 🧩 Cross Device & High Performance Normal Form/Dynamic(JSON Schema) Form/Form Builder -- Support React/React Native/Vue 2/Vue 3
相关推荐
Formily Core ObjectField 模型完全指南:对象字段的动态属性增删与状态管理
Formily Core ObjectField 模型完全指南:对象字段的动态属性增删与状态管理 ObjectField 是 Formily 核心模型体系中专门
前端UI组件Formily React ObjectField 组件深度指南:动态对象字段建模、渲染与源码原理剖析
Formily React ObjectField 组件深度指南:动态对象字段建模、渲染与源码原理剖析 ObjectField 是 Formily 面向 Rea
前端UI组件Formily @formily/core 核心域模型完全指南:从纯内核架构到字段、表单与联动模型
Formily @formily/core 核心域模型完全指南:从纯内核架构到字段、表单与联动模型 导读 @formily/core 是 Formily 全链路
前端UI组件
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考