Formily Vue ObjectField 组件全解析:动态对象结构表单的 ViewModel 桥接实践
前端UI组件【免费下载链接】formily Cross Device High Performance Normal Form/Dynamic(JSON Schema) Form/Form Builder -- Support React/React Native/Vue 2/Vue 3项目地址https://gitcode.com/gh_mirrors/fo/formily点击查看免费下载ObjectField 是 Formily Vue 体系中用于将formily/core的createObjectField字段模型与输入控件做绑定的桥接组件专门解决对象值即一个字段的值本身是一组键值对这类动态结构表单的建模问题。阅读本文后你将掌握 ObjectField 的完整签名、name与 scoped slot 的使用约束、动态增删对象属性的实战方案以及它从 Vue 组件到 core 字段模型的完整实现链路。ObjectField 是什么packages/vue/docs/api/components/object-field.md中明确指出ObjectField 是formily/core的 createObjectField 的 Vue 实现它的职责是将 ViewModel 与输入控件做绑定的桥接组件。通俗地说普通Field绑定的值是一个标量字符串、数字等ArrayField绑定的值是一个数组ObjectField绑定的值是一个对象例如{ title: foo, desc: bar }且对象的键是动态的、运行期可以增删的。因此凡是属性集合不固定、需要动态管理的表单场景——例如动态标签管理、自定义属性编辑器、键值对配置面板——都是 ObjectField 的典型应用场景。它的字段属性field.value、field.addProperty、field.removeProperty等参考IFieldFactoryProps约定在 Vue 侧对应的类型定义可以在 packages/vue/src/types/index.ts 中找到。组件签名与属性类型原文档给出的签名如下type ObjectField Vue.Componentany, any, any, IFieldFactoryProps在 Vue 包的实际类型定义中这一签名被落实为export type IObjectFieldProps IFieldProps即 packages/vue/src/types/index.ts 中IObjectFieldProps直接复用了 core 的IFieldPropsname、basePath、value、initialValue、validator、required、pattern、display、decorator、component、reactions、visible等通用字段属性。同时从 packages/vue/src/vue2-components.ts 可以看到OmitIObjectFieldProps, name还用于派生 JSON Schema 场景下的字段工厂类型说明 ObjectField 的属性体系与普通 Field 保持一致区别仅在于它创建的是对象型字段模型。使用 ObjectField 的两个硬性约束原文档以::: warning的形式特别强调了两条使用纪律这也是新手最容易踩的坑一定要传name属性ObjectField 需要通过name定位它在表单值树中的路径。省略name时字段地址无法正确解析表单值的读写与校验都会失焦。必须使用 scoped slot 形式组织子组件只有通过#default{ field }这样的作用域插槽才能在子组件内部拿到当前字段的field实例进而访问field.value、field.addProperty、field.removeProperty等对象字段专属能力。此外ObjectField 与Field一样需要嵌套在 FormProvider 之下使用以继承表单上下文useForm/useField依赖注入的来源。完整用例动态增删对象属性原文档通过dumi-previewer demoPathapi/components/object-field /引用了一个可运行示例其实际源码位于 packages/vue/docs/demos/api/components/object-field.vue。这是一个非常典型的对象属性编辑器左侧遍历并渲染field.value中的每一个键值输入框右侧提供一个输入框 按钮用于动态追加新属性。完整代码如下template FormProvider :formform ObjectField nameobject template #default{ field } div v-forkey in Object.keys(field.value || {}) :keykey :style{ marginBottom: 10px } Space Field :namekey :component[Input, { placeholder: key }] / Button clickfield.removeProperty(key) Remove /Button /Space /div Space Field namepropertyName basePath required :component[Input, { placeholder: Property Name }] / Button clickaddPropertyToField(field) Add /Button /Space /template /ObjectField /FormProvider /template script import { Input, Space, Button } from ant-design-vue import { createForm } from formily/core import { FormProvider, ObjectField, Field } from formily/vue import ant-design-vue/dist/antd.css export default { components: { FormProvider, ObjectField, Field, Space, Button }, data() { return { Input, form: createForm(), } }, methods: { addPropertyToField(field) { const name this.form.values.propertyName if (name !this.form.existValuesIn(object.${name})) { field.addProperty(name, ) this.form.deleteValuesIn(propertyName) } }, }, } /script逐段拆解这个用例你可以看到几个关键设计作用域插槽拿字段实例template #default{ field }中的field就是 core 的ObjectField模型实例field.value返回当前路径下的整个对象内部实现见下文值即路径读取。遍历渲染Object.keys(field.value || {})动态列出所有键为每个键渲染一个Field。注意这里子Field只传了name它会自动基于父字段地址拼接路径例如object.title这正是父字段提供 basePath机制的体现。删除属性field.removeProperty(key)直接删除该键对应的值同时清理该键下挂载的所有子字段。新增属性先从form.values.propertyName读取要新增的键名用form.existValuesIn(object. name)做重名防抖通过field.addProperty(name, )写入空值最后form.deleteValuesIn(propertyName)清空输入框——这构成一个完整的输入键名 → 追加键值对 → 清空输入闭环。新增输入框放在表单根部basePath让propertyName这个输入字段落在表单顶层/propertyName而不是object.propertyName从而避免与 ObjectField 管理的对象相互干扰。源码级原理从 ObjectField 到 createObjectFieldVue 侧 packages/vue/src/components/ObjectField.ts 的代码非常薄它同时兼容 Vue 2 与 Vue 3基于 vue-demiVue 3 分支setup(props, context)中把fieldType: ObjectField与合并后的fieldProps包含getFieldProps()声明的 props 与getRawComponent(props)抽取的原始组件一起交给ReactiveField渲染Vue 2 分支以functional: true函数式组件的形式完成同样的转发context.children作为插槽内容透传。真正的字段创建发生在 packages/vue/src/components/ReactiveField.ts 中它通过useForm()拿到表单实例然后执行formRef?.value?.[create${props.fieldType}]?.({ ...props.fieldProps, basePath })当fieldType为ObjectField时即调用form.createObjectField(...)。basePath默认取自父字段地址parentRef.value?.address这就是子Field只传name就能自动拼出完整路径的底层原因。ReactiveField还通过useAttach(fieldRef)将字段挂载进表单并通过provide(FieldSymbol, fieldRef)向子孙组件提供字段上下文在渲染阶段mergeSlots会把插槽参数统一补上{ field, form: field.form, ...originArgs[0] }这正是 scoped slot 中能同时拿到field与form的来源。在 core 侧packages/core/src/models/ObjectField.ts 中的ObjectField类继承自Field其值读取直接映射到表单值树get value(): ValueType { return this.form.getValuesIn(this.path) // packages/core/src/models/Field.ts#L326 }也就是说field.value不是字段自身缓存的数据而是表单values中object路径下的实时对象——所有子字段的输入都会立刻反映到field.value上这也是示例中v-forkey in Object.keys(field.value || {})能自动响应的根本原因。ObjectField 专属 APIaddProperty / removeProperty / existProperty在 packages/core/src/models/ObjectField.ts 中ObjectField 相比普通 Field 新增了三个实例方法方法签名行为说明源码位置addProperty(key: string, value: any) void通过form.setValuesIn(this.path.concat(key), value)写入键值并记录到additionalProperties清单最后触发onInputObjectField.tsremoveProperty(key: string) void通过form.deleteValuesIn(this.path.concat(key))删除键值同时从additionalProperties移除该键ObjectField.tsexistProperty(key: string) boolean通过form.existValuesIn(this.path.concat(key))判断属性是否存在ObjectField.ts注意addProperty/removeProperty操作的是表单值树setValuesIn/deleteValuesIn而不是字段本地状态因此对field.value的变更天然响应式界面会立刻重渲染。自动化清理机制makeAutoCleanableObjectField 还内置了一个容易被忽视但非常重要的特性——makeAutoCleanableObjectField.tsprotected 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监听当前对象的键集合变化当某个键从field.value中消失例如被外部form.deleteValuesIn删除、或被联动逻辑清空时会自动清理该键下挂载的所有子字段模型cleanupObjectChildren避免出现值已删除、字段模型仍残留导致的脏状态与内存泄漏。这意味着你用addProperty动态追加的字段其生命周期完全由 ObjectField 托管无需手动销毁。测试验证与更多场景Vue 包针对 ObjectField 的绑定行为有对应的单元测试覆盖例如 packages/vue/src/tests/field.spec.ts 与 packages/vue/src/tests/schema.markup.spec.ts 中均涉及 ObjectField 的创建与渲染断言可作为阅读和调试的参考入口。在 Formily 的字段家族中ObjectField 与 ArrayField数组型、VoidField无值容器型共同覆盖了复杂嵌套结构的建模需求。如果你的数据是一个键集合固定的普通对象直接用Field JSON Schema 即可只有当键需要在运行期动态增删、或需要对对象整体做联动与校验控制时才需要引入 ObjectField。小结ObjectField 是 Formily Vue 处理动态对象结构的核心桥接组件使用上牢记必传name scoped slot 组织子组件两条铁律实战中借助field.value遍历、addProperty/removeProperty增删、existProperty判重即可搭建完整属性编辑器原理上它经由 ObjectField.ts →ReactiveField→form.createObjectField完成字段模型创建由 core 的ObjectField类提供值映射、动态增删与自动清理能力是一套薄封装、强模型的典型实现。赞分享前端UI组件【免费下载链接】formily Cross Device High Performance Normal Form/Dynamic(JSON Schema) Form/Form Builder -- Support React/React Native/Vue 2/Vue 3项目地址https://gitcode.com/gh_mirrors/fo/formily点击查看免费下载相关推荐Formily React ObjectField 组件深度指南动态对象字段建模、渲染与源码原理剖析Formily React ObjectField 组件深度指南动态对象字段建模、渲染与源码原理剖析 ObjectField 是 Formily 面向 Rea前端UI组件Formily Core ObjectField 模型完全指南对象字段的动态属性增删与状态管理Formily Core ObjectField 模型完全指南对象字段的动态属性增删与状态管理 ObjectField 是 Formily 核心模型体系中专门前端UI组件Formily React 字段桥接组件 Field 完全指南ViewModel 与输入控件的绑定原理与实战Formily React 字段桥接组件 Field 完全指南ViewModel 与输入控件的绑定原理与实战 导读 在 Formily 的 React 体系中前端UI组件创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考