ARTICLE DETAIL

资讯详情

深耕网站建设、视觉设计与SEO优化的一线实战洞察。

Formily Vue VoidField 组件完全指南:虚拟布局字段的桥接、显隐控制与状态恢复机制

Formily Vue VoidField 组件完全指南:虚拟布局字段的桥接、显隐控制与状态恢复机制 前端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点击查看免费下载导读VoidField 是 Formily 在 Vue 体系中用于桥接 ViewModel 与虚拟布局控件的核心组件它本身不绑定任何业务数据而是作为容器控制数据型字段Field的显示隐藏、交互模式pattern与嵌套关系。本文以 packages/vue/docs/api/components/void-field.md 为骨架结合 formily/vue 与 formily/core 的源码实现和测试用例完整讲解 VoidField 的组件签名、全部 Props 语义、显隐切换时值被清空又可恢复现场的底层机制以及它在真实场景中的写法。读完你将掌握如何用 VoidField 包裹布局与数据字段、如何通过visible/hidden/display精确控制显示层级、以及为什么display: none会触发值的缓存与恢复。VoidField 是什么根据官方文档VoidField 是 formily/core 中 createVoidField 的 Vue 实现对应源码位置 packages/core/src/models/Form.ts#L361-L377。它专门用于将 ViewModel 与虚拟布局控件做绑定的桥接组件可以用来控制数据型字段的显示隐藏、交互模式等。与 Field 最大的区别在于Field绑定一个真实的数据节点拥有value、validator、dataSource等数据相关状态VoidField是一个虚拟字段不产生任何表单数据只承载display显隐与pattern交互模式状态并把它们沿字段树向下传递影响其包裹的所有数据型字段。从 formily/vue 的类型定义可以看到VoidField 的 Props 完全继承自 core 的IVoidFieldFactoryProps见 packages/vue/src/types/index.ts#L41-L44export type IVoidFieldProps D extends VueComponent VueComponent, C extends VueComponent VueComponent IVoidFieldFactoryPropsD, C组件签名官方文档给出的签名如下type VoidField Vue.Componentany, any, any, IVoidFieldFactoryProps这意味着 VoidField 是一个泛型组件其 Props 类型即 core 中的IVoidFieldFactoryProps。在 Vue 2 中它以函数式组件functional实现在 Vue 3 中则通过setup实现但最终都渲染到同一个内部组件ReactiveField见 packages/vue/src/components/VoidField.ts。必传 name 属性::: warning 我们在使用 VoidField 组件的时候一定要记得传 name 属性。 :::这是文档中的硬性警告其背后有明确的源码依据。看 packages/core/src/models/Form.ts#L361-L377 中createVoidField的实现createVoidField Decorator extends JSXComponent, Component extends JSXComponent ( props: IVoidFieldFactoryPropsDecorator, Component ): VoidFieldDecorator, Component { const address FormPath.parse(props.basePath).concat(props.name) const identifier address.toString() if (!identifier) return ... }字段的地址address由basePath与name拼接而成。如果name为空identifier就是空字符串createVoidField会直接返回undefined后续对该字段的任何查询和状态操作都将失效。测试用例 packages/core/src/tests/form.spec.ts#L69 中的expect(form.createVoidField({ name: })).toBeUndefined()正是对这一行为的验证。因此即使 VoidField 不产生数据也必须给它一个唯一且有意义的 name例如布局层的名字layout、grid、card等它将成为后续通过form.query()检索该字段的路径标识。完整 Props 列表与语义VoidField 在 Vue 中实际声明的 Props 全部定义在 packages/vue/src/utils/getFieldProps.ts#L25-L41 的getVoidFieldProps中export const getVoidFieldProps () ({ name: {}, title: {}, description: {}, basePath: {}, decorator: Array, component: Array, display: String, pattern: String, hidden: { type: Boolean, default: undefined }, visible: { type: Boolean, default: undefined }, editable: { type: Boolean, default: undefined }, disabled: { type: Boolean, default: undefined }, readOnly: { type: Boolean, default: undefined }, readPretty: { type: Boolean, default: undefined }, reactions: [Array, Function], })对照 Field 的 PropsgetFieldProps可以发现VoidField剔除了所有数据相关属性value、initialValue、required、validateFirst、dataSource、validator只保留布局与交互相关的部分。各属性语义如下Prop类型说明nameany必填字段路径标识决定该 VoidField 在字段树中的位置titlestring标题可被子节点继承用于展示descriptionstring描述信息basePathstring基础路径不传时自动取父级字段的 address见 ReactiveField.ts#L126-L130decorator[Component, Props?]装饰器组件用于包裹子节点渲染如卡片、折叠面板等布局控件component[Component, Props?]组件VoidField 也可直接渲染一个虚拟组件如 Grid、Spacedisplayvisible \| hidden \| none显示状态最底层的显隐开关patterneditable \| disabled \| readOnly \| readPretty交互模式向下传递给子字段hiddenboolean快捷设置true→display hiddenfalse→display visiblevisibleboolean快捷设置false→display nonetrue→display visibleeditableboolean快捷设置false→pattern readPrettydisabledboolean快捷设置true→pattern disabledreadOnlyboolean快捷设置true→pattern readOnlyreadPrettyboolean快捷设置true→pattern readPrettyreactionsArray | Function字段联动逻辑display 与 pattern 的底层映射visible/hidden/editable/disabled/readOnly/readPretty这些布尔快捷属性本质上是display和pattern两个核心状态的语法糖映射逻辑实现在 packages/core/src/models/BaseField.ts#L160-L219get hidden() { return this.display hidden } get visible() { return this.display visible } set hidden(hidden: boolean) { if (hidden) { this.display hidden } else { this.display visible } } set visible(visible: boolean) { if (visible) { this.display visible } else { this.display none // 注意visiblefalse 对应 none 而非 hidden } }由此可以梳理出三个显隐层级的关键区别display: visible正常显示display: hidden保留占位控件不可见但仍在文档流中字段值不会被清除display: none完全移除不占任何空间字段值会被清空并缓存再次显示时恢复。这一点在 core 的单元测试 packages/core/src/tests/void.spec.ts#L36-L46 中也有直接验证const field3 attach( form.createVoidField({ name: field3, readOnly: true, visible: false, }) ) expect(field3.pattern).toEqual(readOnly) expect(field3.display).toEqual(none) // visible:false → display:none同理pattern的四个取值通过disabled/readOnly/readPretty/editable的 setter 互相转换最终都会落到editable | disabled | readOnly | readPretty四选一并沿字段树向下传导。实战用例用 VoidField 控制子节点显隐官方文档给出的用例演示了 VoidField 最典型的场景——控制子节点显示隐藏。完整可运行的示例位于 packages/vue/docs/demos/api/components/void-field.vue其核心结构如下template FormProvider :formform Space VoidField namelayout Field nameinput :component[Input] / /VoidField FormConsumer template #default{ form } Space Button click () { form .query(layout) .take() .setState((state) { state.visible !state.visible }) } {{ form.query(layout).get(visible) ? Hide : Show }} /Button div{{ JSON.stringify(form.values, null, 2) }}/div /Space /template /FormConsumer /Space /FormProvider /template script import { Input, Space, Button } from ant-design-vue import { createForm } from formily/core import { FormProvider, Field, FormConsumer, VoidField } from formily/vue import ant-design-vue/dist/antd.css export default { components: { FormProvider, Field, FormConsumer, VoidField, Space, Button }, data() { return { Input, form: createForm(), } }, } /script逐行拆解VoidField namelayout创建一个名为layout的虚拟字段它不产生任何表单数据仅作为容器Field nameinput :component[Input] /真正的数据型字段被嵌套在 VoidField 内部其路径为layout.input值会写入form.values.layout.inputform.query(layout).take().setState(...)通过 Formily 的查询 API 找到layout这个 VoidField 模型实例然后以setState修改其状态state.visible !state.visible切换visible触发display在visible与none之间的跳变form.query(layout).get(visible)读取当前visible计算属性用于渲染按钮文案Hide/Show。观察到的行为运行该示例可以看到两个关键现象这也是文档强调的核心特性VoidField 隐藏时子节点的数据会同时被清空。因为visible false代表display none这种隐藏是彻底移除不会保留字段值再次显示时又会恢复现场。这是 Formily Core 内部的特性支持完全恢复现场的能力——清空只是暂时性的值并没有真正丢失。底层机制display: none 时的值缓存与恢复为什么display: none会清空值恢复显示时又能还原现场这得益于 formily/core 中 Field 模型内置的值缓存caches.value机制实现位于 packages/core/src/models/Field.ts 的两处 reaction1. display 变化时缓存/恢复值Field.ts#L244-L264createReaction( () this.display, (display) { const value this.value if (display ! none) { // 从 none 恢复为可见把缓存的值写回 if (value undefined this.caches.value ! undefined) { this.setValue(this.caches.value) this.caches.value undefined } } else { // 变为 none缓存当前值并从 form.values 中删除 this.caches.value toJS(value) ?? toJS(this.initialValue) this.form.deleteValuesIn(this.path) } if (display none || display hidden) { this.setFeedback({ type: error, messages: [], }) } } )当display变为none当前值或 initialValue被快照到caches.value同时form.deleteValuesIn(this.path)将其从表单数据中删除当display恢复为非none如果当前值为空而缓存非空则把缓存值写回。注意hidden并不会触发删除display ! none分支这印证了hidden保留占位、值不清除的语义。2. none 状态下写入值时的拦截Field.ts#L220-L237createReaction( () this.value, (value) { this.notify(LifeCycleTypes.ON_FIELD_VALUE_CHANGE) if (isValid(value)) { if (this.selfModified !this.caches.inputting) { validateSelf(this) } if (!isEmpty(value) this.display none) { this.caches.value toJS(value) this.form.deleteValuesIn(this.path) } } } )即使在display: none期间外部仍然向该字段写入值值也会被立即转入缓存并从form.values移除保证隐藏状态下表单数据始终干净。3. setValues 批量赋值时的保护同样地packages/core/src/shared/internals.ts#L201-L214 的patchFormValues在批量赋值时也会做保护const targetField form.query(path).take() const isUnVoidField targetField !isVoidField(targetField) if (isUnVoidField targetField.display none) { targetField.caches.value clone(source) return }即对处于none状态的非 Void 字段外部赋值的值只会进入caches.value不会污染form.values。这就是隐藏即清空、恢复即还原机制的完整闭环。源码剖析VoidField 组件是如何工作的在 formily/vue 中VoidField 只是薄薄的一层壳真正的字段创建与渲染逻辑在ReactiveField中。调用链如下VoidFieldVue 组件 └─ ReactiveFieldfieldType: VoidField ├─ form.createVoidField({ ...fieldProps, basePath }) ├─ useAttach(fieldRef) // 挂载/卸载生命周期 ├─ provide(FieldSymbol) // 向子节点暴露父级字段 └─ 渲染display ! visible 时输出空模板VoidField 的双端实现packages/vue/src/components/VoidField.ts 通过vue-demi同时兼容 Vue 2 与 Vue 3Vue 2声明为functional: true的函数式组件在render中把fieldType: VoidField与合并后的fieldProps传给ReactiveFieldVue 3在setup中返回渲染函数同样构造{ fieldType, fieldProps }数据传给ReactiveField。两者最终都执行_h(ReactiveField, componentData, children)因此无论哪个 Vue 版本行为完全一致。ReactiveField 的字段创建packages/vue/src/components/ReactiveField.ts#L122-L142 负责真正的字段实例化setup(props: IReactiveFieldProps, { slots }) { const formRef useForm() const parentRef useField() let createField () formRef?.value?.[create${props.fieldType}]?.({ ...props.fieldType VoidField ? props.fieldProps : { ...props.fieldProps, basePath: props.fieldProps?.basePath ?? parentRef.value?.address }, }) ... const fieldRef shallowRef(createField()) as RefGeneralField watch( () props.fieldProps, () (fieldRef.value createField()) ) useAttach(fieldRef) provide(FieldSymbol, fieldRef) }关键点通过动态方法名form[createVoidField]调用 core 的工厂方法props 中的basePath缺省时自动取父级字段的address保证字段树嵌套关系正确useAttach见 packages/vue/src/hooks/useAttach.ts在组件onMounted时调用字段的onMount()onUnmounted时调用onUnmount()并监听字段替换时的回收provide(FieldSymbol, fieldRef)把字段实例注入到子孙组件这是useField等组合式 API 能拿到上下文的原因。渲染与显隐控制ReactiveField.ts#L143-L151return () { const field fieldRef.value if (!field) { return slots.default?.() } if (field.display ! visible) { return h(template, {}, {}) } ... }ReactiveField本身是一个observer组件来自 formily/reactive-vue任何被读取的响应式状态变化都会触发重渲染。当display不是visible时直接渲染一个空template子节点不产生任何 DOM。这也是 VoidField 控制整棵子树显隐的渲染层依据——父级不可见子级自然全部消失。VoidField 模型的状态定义core 侧的 packages/core/src/models/VoidField.ts 继承BaseField在initialize中接收display、pattern、hidden、visible、disabled、readOnly、readPretty、content、decorator、component等 props并通过makeObservable把display/pattern等声明为observable.computed把setDisplay/setPattern/setComponent/setDecorator等声明为action从而与 formily/reactive 的响应式系统无缝衔接。联动VoidField 的 reactionsVoidField 同样支持reactions属性可以在别的字段状态变化时驱动 VoidField 的显隐与交互模式。core 测试 packages/core/src/tests/void.spec.ts#L229-L250 演示了典型写法form.createVoidField({ name: bb, reactions: [ (field) { const aa field.query(aa) if (aa.get(value) 123) { field.visible false } else { field.visible true } if (aa.get(inputValue) 333) { field.editable false } else if (aa.get(inputValue) 444) { field.editable true } }, ], })reaction 函数接收字段实例field通过field.query(aa)查询其他字段再根据其value或inputValue修改visible/editable等状态。这是实现字段 A 变化 → 布局区域 B 显隐/只读这类联动逻辑的标准方式。嵌套显隐与 pattern 的向下传导VoidField 的显隐和交互模式会沿字段树向下传导这一点在 packages/core/src/tests/void.spec.ts#L165-L227 的nested display/pattern测试中有完整覆盖const void_ attach(form.createVoidField({ name: void, basePath: object })) const aaa attach(form.createField({ name: aaa, basePath: object.void })) const bbb attach(form.createField({ name: bbb, basePath: object.void })) void_.setPattern(readPretty) expect(aaa.pattern).toEqual(readPretty) // 子字段同步变为 readPretty expect(bbb.pattern).toEqual(readPretty) void_.setDisplay(none) expect(aaa.display).toEqual(none) // 子字段同步变为 none expect(bbb.display).toEqual(none) void_.setDisplay() expect(void_.display).toEqual(visible) // 重置回 visible expect(aaa.display).toEqual(visible)这解释了 VoidField 作为布局控制器的设计精髓你只需要在虚拟节点上设置一次显隐或交互模式其下所有数据型字段无论嵌套多深都会自动继承无需逐个字段操作。与 Field / ObjectField 的选择建议场景推荐组件需要绑定一个数据节点、承载值/校验Field需要组织对象结构、管理对象的子字段ObjectField需要布局分组、显隐控制、交互模式传导但不产生数据VoidField一句话总结凡是不需要存数据的壳卡片、分组、网格、步骤容器、显隐开关的宿主都应该用 VoidField凡是需要数据的叶子节点才用 Field。合理使用 VoidField能让你的 Formily 表单结构更清晰显隐与联动逻辑也更易于维护。关键文件速查官方文档packages/vue/docs/api/components/void-field.md官方用例packages/vue/docs/demos/api/components/void-field.vueVue 组件实现packages/vue/src/components/VoidField.ts、packages/vue/src/components/ReactiveField.tsProps 定义packages/vue/src/utils/getFieldProps.ts#L25-L41字段工厂packages/core/src/models/Form.ts#L361-L377核心模型packages/core/src/models/VoidField.ts、packages/core/src/models/BaseField.ts#L160-L219显隐值缓存机制packages/core/src/models/Field.ts#L220-L264、packages/core/src/shared/internals.ts#L201-L214测试验证packages/core/src/tests/void.spec.ts赞分享前端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 VoidField 组件完全指南用虚拟字段控制布局、显隐与交互状态Formily React VoidField 组件完全指南用虚拟字段控制布局、显隐与交互状态 导读 VoidField虚拟字段是 Formily 体系中前端UI组件Formily React 中 VoidField 完全指南虚拟布局节点的创建、隐藏与场景恢复机制Formily React 中 VoidField 完全指南虚拟布局节点的创建、隐藏与场景恢复机制 导读 VoidField 是 Formily 表单体系中专前端UI组件Formily 核心包 VoidField 模型完全指南无数据字段的布局容器与联动控制Formily 核心包 VoidField 模型完全指南无数据字段的布局容器与联动控制 VoidField虚字段是 Formily 核心包 formi前端UI组件创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表