Vant 中 useCustomFieldValue 使用指南:为 Form 自定义表单项注入值
【免费下载链接】vantA lightweight, customizable Vue UI library for mobile web apps.项目地址: https://gitcode.com/GitHub_Trending/va/vant
本指南围绕 Vant 移动端组件库提供的useCustomFieldValue组合式函数,讲解如何在 van-field 的input插槽中插入自定义组件,并让该组件的值参与 Form 的取值与校验流程。读完本文,你将掌握自定义表单项的完整写法、其基于 provide/inject 的底层工作原理,以及它如何自动联动校验、重置与表单提交。
为什么需要自定义表单项
Vant 的 Form 在提交时会遍历所有子级 Field,通过getValues按name收集每个表单项的值:
// packages/vant/src/form/Form.tsx const getValues = () => children.reduce<Record<string, unknown>>((form, field) => { if (field.name !== undefined) { form[field.name] = field.formValue.value; } return form; }, {});而每个 Field 的formValue并不是简单地取modelValue,它会优先读取“自定义值”。当你在input插槽中放置了自己的组件时,Form 实际收集到的值就来自该自定义组件——这正是useCustomFieldValue的用武之地。
基本用法
原文档给出的完整流程分为两步:先在自定义组件内调用useCustomFieldValue并传入取值回调,再把自定义组件放进 Field 的input插槽。
第一步:自定义组件中注册取值函数
在自定义组件(如MyComponent.vue)的setup中调用useCustomFieldValue,传入一个返回表单项值的回调函数:
// MyComponent.vue import { ref } from 'vue'; import { useCustomFieldValue } from '@vant/use'; export default { setup() { const myValue = ref(0); // 此处传入的值会替代 Field 组件内部的 value useCustomFieldValue(() => myValue.value); return { myValue }; }, };回调返回的值可以是任意类型(unknown),不仅限于字符串——数字、对象、数组都支持,表单提交时该值会被原样写入结果。
第二步:在 Field 的 input 插槽中挂载自定义组件
在 Form 中通过van-field的name属性指定该字段在提交结果中的键名,并在#input插槽中渲染自定义组件:
<van-form> <!-- 这是一个自定义表单项 --> <!-- 当表单提交时,会包括 useCustomFieldValue 中传入的值 --> <van-field name="my-field" label="自定义表单项"> <template #input> <my-component /> </template> </van-field> </van-form>提交表单后,submit事件回调中的values对象将包含{ 'my-field': <myValue 的值> }。
注意:Field 的
input插槽说明(见 packages/vant/src/field/README.zh-CN.md)指出,使用此插槽后,与原生输入框相关的属性和事件将失效(如maxlength、clearable等输入行为由你的自定义组件接管)。Form 的 README 也专门提到“Vant 支持在 Form 组件中插入自定义的表单项”(见 packages/vant/src/form/README.zh-CN.md)。
底层原理:一次 provide / inject 协作
Field 侧:注入上下文
Field 在 setup 中创建了一个customValue引用,并通过 Vue 的provide向所有子组件暴露校验与重置能力(见 packages/vant/src/field/Field.tsx):
provide(CUSTOM_FIELD_INJECTION_KEY, { customValue, resetValidation, validateWithTrigger, });对应的formValue计算属性则优先采用自定义值:
// packages/vant/src/field/Field.tsx const formValue = computed(() => { if (customValue.value && slots.input) { return customValue.value(); } return props.modelValue; });也就是说:只有同时满足“子组件已注册自定义值”和“存在 input 插槽”两个条件时,Field 提交给 Form 的值才会取自自定义组件,否则回退到常规的modelValue。
useCustomFieldValue 侧:注入并注册
useCustomFieldValue的实现位于 packages/vant-use/src/useCustomFieldValue/index.ts,核心逻辑如下:
export const CUSTOM_FIELD_INJECTION_KEY: InjectionKey<CustomFieldInjectionValue> = Symbol('van-field'); export function useCustomFieldValue(customValue: () => unknown) { const field = inject(CUSTOM_FIELD_INJECTION_KEY, null); if (field && !field.customValue.value) { field.customValue.value = customValue; watch(customValue, () => { field.resetValidation(); field.validateWithTrigger('onChange'); }); } }几个关键点值得注意:
- 注入键:
CUSTOM_FIELD_INJECTION_KEY是Symbol('van-field')。如果组件不在 Field 的input插槽上下文中(inject返回null),调用会被静默忽略,因此务必把自定义组件放在 Field 内部。 - 只注册一次:
if (field && !field.customValue.value)保证每个 Field 只接受第一个注册的回调,重复调用不会覆盖已注册的值。 - 自动联动校验:
watch(customValue, ...)监听取值回调的依赖变化(如myValue.value改变),随后自动执行resetValidation()清空旧校验状态,并调用validateWithTrigger('onChange')按onChange触发时机重新校验(Form 的validateTrigger默认是onBlur,见 packages/vant/src/form/Form.tsx)。这意味着自定义组件内部值变化时,校验、错误提示与提交值会同步更新,无需手动干预。
Form 侧:收集与提交
Form 在submit时先validate()全部字段,再触发submit事件携带values(见 packages/vant/src/form/Form.tsx)。由于getValues读取的是field.formValue.value,而该计算属性在自定义场景下返回的是你注册的回调结果,所以自定义表单项自然被纳入校验与取值链路;校验失败时同样会触发failed事件,并可配合scrollToError滚动定位。
API 参考
useCustomFieldValue的类型定义与参数如下(原文档 API 部分):
function useCustomFieldValue(customValue: () => unknown): void;| 参数 | 说明 | 类型 | 默认值 |
|---|---|---|---|
| customValue | 获取表单项值的函数 | () => unknown | - |
该函数返回void,没有返回值,也不需要在模板中引用;它只负责把取值回调注册给最近的 Field 父级。
内置组件中的实际应用
useCustomFieldValue并非只服务于第三方自定义组件,Vant 自身的多个表单类组件在放入 Field 的input插槽时也复用了这套机制,例如:
- Switch:
useCustomFieldValue(() => props.modelValue) - Checkbox 与 CheckboxGroup
- RadioGroup
- Stepper
- Slider
- Rate
- Uploader
也就是说,当你写下<van-field><template #input><van-switch v-model="xxx" /></template></van-field>时,Switch 正是通过useCustomFieldValue把自己的modelValue注册给 Field,从而让 Form 的提交结果与校验逻辑正确拿到开关状态。这从侧面验证了该函数的通用性与正确用法:任意具备内部状态、且能提供取值函数的组件,都可以借此成为 Form 的一员。
最佳实践与注意事项
- 取值回调必须可被响应式追踪:
watch(customValue, ...)依赖回调在求值过程中访问的响应式依赖(如ref)。若回调返回的是固定常量,则值永远不会变化,也就不会触发重新校验;应确保回调内部引用ref/reactive等响应式状态。 - 放置位置决定上下文:
useCustomFieldValue必须在 Field 组件树内调用(通常是input插槽中的子组件),否则inject取不到 Field 提供的上下文,注册会静默失败。 - 一个 Field 只注册一个自定义值:由于实现中只写入首个回调,若插槽内存在多个组件同时调用,后注册者将被忽略。如需组合多个状态,可在单个回调中聚合(例如返回一个对象)。
- 校验规则仍定义在 Field 上:自定义表单项的
rules、required等校验配置照常写在van-field上,Form 校验时使用的是自定义值,因此pattern、validator拿到的value就是自定义回调的返回值(空值判断逻辑见 packages/vant/src/field/utils.ts 中的isEmptyValue与runSyncRule)。 - 值的类型自由:回调返回
unknown,意味着你可以让 Form 直接收集对象、数组等复合结构,而不必像原生输入那样先序列化为字符串。
小结
useCustomFieldValue是 Vant 打通“自定义 UI”与“标准表单能力”的关键桥梁:通过Symbol('van-field')注入键 +provide/inject上下文,把自定义组件的值无缝接入 Field 的formValue计算链路,并在值变化时自动重置与触发校验。无论是自研组件还是像 Switch、Slider、Rate 这类内置组件,都能借助它获得与原生表单项一致的提交、校验体验。
【免费下载链接】vantA lightweight, customizable Vue UI library for mobile web apps.项目地址: https://gitcode.com/GitHub_Trending/va/vant
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考