ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

TanStack Vue Form 动态校验(Dynamic Validation)实战指南:用 revalidateLogic 与 onDynamic 按提交状态切换校验策略

TanStack Vue Form 动态校验(Dynamic Validation)实战指南:用 revalidateLogic 与 onDynamic 按提交状态切换校验策略 TanStack Vue Form 动态校验Dynamic Validation实战指南用 revalidateLogic 与 onDynamic 按提交状态切换校验策略【免费下载链接】form Headless, performant, and type-safe form state management for TS/JS, React, Vue, Angular, Solid, and Lit.项目地址: https://gitcode.com/GitHub_Trending/form/form导读在真实表单场景中校验规则往往需要因时制宜用户第一次提交前输入提示不应过于激进地打断填写而一旦提交过一次就应该在后续每次改动或失焦时立刻给出反馈。TanStack Vue Form 通过onDynamic校验器与revalidateLogic校验策略函数实现了这套先松后严的动态校验机制。本文以 docs/framework/vue/guides/dynamic-validation.md 为骨架结合 ValidationLogic.ts 的源码实现与 DynamicValidation.spec.ts 的测试用例带你掌握revalidateLogic的两种模式参数、onDynamic在表单与字段两个层级的用法、错误读取方式以及异步与 Standard SchemaZod/Valibot集成等完整实践。什么是动态校验为什么需要onDynamic常规校验器onChange、onBlur、onSubmit的触发时机是静态写死的值一变就跑、失焦就跑、提交才跑。但在许多业务中你希望根据表单当前所处的阶段尤其是是否已经提交过动态决定执行哪套校验。最典型的场景是用户首次提交前只在提交时校验避免每敲一个字符就报错打扰填写流程用户首次提交后转为实时校验模式改动即校验、失焦即校验第一时间纠正错误。TanStack Vue Form 通过onDynamic校验器支持这种动态行为它由useForm的validationLogic选项驱动。validationLogic是一个可插拔的校验策略函数框架默认使用 defaultValidationLogic它按事件类型固定地挑选onChange/onBlur/onSubmit/onMount校验器执行而revalidateLogic则是专为动态校验设计的策略只负责调度onDynamic校验器。在 FormApi.ts 中可以看到validationLogic选项的类型定义为ValidationLogicFn而 FormApi.ts 显示当未显式传入时默认回退为defaultValidationLogic——这正是不传validationLogic时onDynamic永远不会被调用的底层原因。快速上手表单级onDynamic校验onDynamic与onChange、onBlur一样可以配置在useForm的validators对象上。它的回调签名也一致接收({ value })返回undefined通过或错误对象表单级校验的错误按字段名组织。第一步必须通过validationLogic: revalidateLogic()开启动态校验调度script setup langts import { revalidateLogic, useForm } from tanstack/vue-form const form useForm({ defaultValues: { firstName: , lastName: , }, // 如果省略这行onDynamic 永远不会被调用 validationLogic: revalidateLogic(), validators: { onDynamic: ({ value }) { if (!value.firstName) { return { firstName: A first name is required } } return undefined }, }, }) /script官方文档明确提醒默认情况下onDynamic不会被调用必须把revalidateLogic()传给useForm的validationLogic选项。底层机制revalidateLogic是如何调度的从 ValidationLogic.ts 的实现可以看出revalidateLogic()返回一个ValidationLogicFn在每次校验事件发生时执行以下逻辑从props.validators中挑出onDynamic同步事件或onDynamicAsync异步事件标记cause: dynamicValidationLogic.ts读取submissionAttempts提交次数判断当前是否已提交过submissionAttempts 0时使用mode否则使用modeAfterSubmissionValidationLogic.ts仅当事件类型命中[modeToWatch, submit]时才把onDynamic校验器加入执行队列ValidationLogic.ts通过defaultValidationLogic计算常规校验器列表最后将两者合并交给props.runValidation执行ValidationLogic.ts。也就是说revalidateLogic不是替换掉常规校验而是额外按阶段把onDynamic插进校验队列——这一点与下文与其他校验逻辑共存一节完全吻合。submissionAttempts状态在 FormApi.ts 中于每次提交时1递增。revalidateLogic 的重校验选项Revalidation OptionsrevalidateLogic接受两个参数用来分别指定首次提交前与首次提交后的校验触发时机参数可选值默认值含义modechange/blur/submitsubmit表单首次提交之前的校验模式modeAfterSubmissionchange/blur/submitchange表单首次提交之后的校验模式各取值的含义change每次值变化时都执行校验blur字段失焦时执行校验submit仅在提交时执行校验。默认组合即提交前只在提交时校验提交后每次改动都校验——这正是 React Hook Form 风格的modereValidateMode语义源码注释也明确指出这一点见 ValidationLogic.ts。例如希望首次提交后改为失焦即重新校验script setup langts const form useForm({ // ... validationLogic: revalidateLogic({ mode: submit, modeAfterSubmission: blur, }), // ... }) /script在 DynamicValidation.spec.ts 中有一组针对mode: changemodeAfterSubmission: blur的测试完整验证了四种时序提交前改动即报错 → 提交后改动不再即时报错 → 提交后失焦才清除/触发错误与上表语义一一对应。从源码看调度细节当props.event.type为submit时无论处于哪个阶段onDynamic都会被强制执行[modeToWatch, submit].includes(...)确保提交时总是全量校验mode的默认值submit、modeAfterSubmission的默认值change均由 ValidationLogic.ts 中RevalidateLogicProps接口的default注释声明对FormGroupApi的校验源码会根据props.group是否存在改用组自身的state.meta.submissionAttempts而不是父表单的提交次数来判定阶段ValidationLogic.ts保证字段组内的动态校验在组首次提交后才切换为modeAfterSubmission。读取 onDynamic 校验错误onDynamic产生的错误与其它校验器完全一致统一存放在表单的form.state.errorMap对象中键为onDynamic。表单级onDynamic返回的对象按字段名组织因此可以这样读取并渲染script setup langts const form useForm({ // ... validationLogic: revalidateLogic(), validators: { onDynamic: ({ value }) { if (!value.firstName) { return { firstName: A first name is required } } return undefined }, }, }) /script template p{{ form.state.errorMap.onDynamic?.firstName }}/p /template底层的错误映射关系由 FormApi.ts 中的getErrorMapKey函数维护cause: dynamic会被映射到errorMap.onDynamic键。在 DynamicValidation.spec.ts 中异步校验的错误也通过form.state.errorMap.onDynamic断言确认读写路径一致。与其他校验逻辑共存onDynamic不是二选一的替代品它可以与onChange、onBlur等常规校验器同时使用。revalidateLogic内部会先通过defaultValidationLogic计算出常规校验器队列再把onDynamic追加进去合并执行ValidationLogic.ts对应测试见 DynamicValidation.spec.ts。例如让firstName走实时onChange校验、lastName走动态onDynamic校验script setup langts import { revalidateLogic, useForm } from tanstack/vue-form const form useForm({ defaultValues: { firstName: , lastName: , }, validationLogic: revalidateLogic(), validators: { onChange: ({ value }) { if (!value.firstName) { return { firstName: A first name is required } } return undefined }, onDynamic: ({ value }) { if (!value.lastName) { return { lastName: A last name is required } } return undefined }, }, }) /script template div p{{ form.state.errorMap.onChange?.firstName }}/p p{{ form.state.errorMap.onDynamic?.lastName }}/p /div /template注意错误会按各自的来源键分开存放errorMap.onChange、errorMap.onDynamic互不覆盖。与字段Field一起使用onDynamic同样支持字段级配置用法与其它字段校验器一致。字段级校验回调返回字符串错误信息而非按字段名组织的对象读取路径是field.state.meta.errorMap.onDynamic。下面示例中age字段在首次提交后才会激活必须大于 18的校验而name字段则保持宽松script setup langts const form useForm({ defaultValues: { name: , age: 0, }, validationLogic: revalidateLogic(), onSubmit({ value }) { alert(JSON.stringify(value)) }, }) /script template form submit (e) { e.preventDefault() e.stopPropagation() form.handleSubmit() } form.Field nameage :validators{ onDynamic: ({ value }) value 18 ? undefined : Age must be greater than 18, } template v-slot{ field } div input typenumber :valuefield.state.value input (e) field.handleChange((e.target as HTMLInputElement).valueAsNumber) blurfield.handleBlur / p stylecolor: red {{ field.state.meta.errorMap.onDynamic }} /p /div /template /form.Field button typesubmitSubmit/button /form /template字段级onDynamic的行为在 DynamicValidation.spec.ts 中有完整测试提交前setValue(Jo)不产生错误handleSubmit()之后错误出现再setValue(Joe123)错误立即清除——正好验证了首次提交前 submit 模式、提交后 change 模式的动态切换。异步动态校验Async ValidationonDynamic也支持异步校验通过onDynamicAsync声明并且可以像onChangeAsyncDebounceMs一样使用onDynamicAsyncDebounceMs做防抖避免高频输入时反复发起网络请求script setup langts const form useForm({ defaultValues: { username: , }, validationLogic: revalidateLogic(), validators: { onDynamicAsyncDebounceMs: 500, // 将异步校验防抖 500ms onDynamicAsync: async ({ value }) { if (!value.username) { return { username: Username is required } } // 模拟异步校验例如调用后端检查用户名是否已被占用 const isValid await validateUsername(value.username) return isValid ? undefined : { username: Username is already taken } }, }, }) /script从源码看当props.event.async为true时revalidateLogic会改选onDynamicAsync作为动态校验器ValidationLogic.ts。onDynamicAsyncDebounceMs等防抖配置项与onDynamicAsync、onDynamic一起定义在 FormApi.ts 的FormValidators接口中。异步路径的完整行为提交前不校验 → 提交后报错 → 改动后清除在 DynamicValidation.spec.ts 与字段级异步测试DynamicValidation.spec.ts中均有覆盖。集成 Standard Schema 校验库Zod / ValibotonDynamic同样支持 Standard Schema 兼容的校验库如 Valibot、Zod可以把复杂规则直接作为onDynamic的值传入。这意味着你的 schema 规则可以随表单阶段动态生效而规则本身依然保持声明式、可复用script setup langts import { z } from zod const schema z.object({ firstName: z.string().min(1, A first name is required), lastName: z.string().min(1, A last name is required), }) const form useForm({ defaultValues: { firstName: , lastName: , }, validationLogic: revalidateLogic(), validators: { onDynamic: schema, }, }) /script源码层面DynamicValidation.spec.ts 的rhf validation should work as-expected用例正是用z.object({...})作为onDynamic的取值验证了提交前改动不报错 → 提交后立即报错 → 修正后错误清除的完整流程与手写回调函数的动态行为完全一致。Standard Schema 的解析与错误归一化由 standardSchemaValidator.ts 提供onDynamic同样经由该通道处理。完整行为一览与最佳实践结合文档与 DynamicValidation.spec.ts 测试revalidateLogic()默认参数下的onDynamic完整时序如下阶段触发事件onDynamic 是否执行首次提交前值变化change否首次提交前失焦blur否首次提交前提交submit是mode生效默认 submit首次提交后值变化change是modeAfterSubmission生效默认 change首次提交后失焦blur视modeAfterSubmission而定首次提交后再次提交submit总是执行实践建议记得配置validationLogic不使用revalidateLogic()或自定义的ValidationLogicFn时onDynamic永远不会被触发这是最常见的校验不生效原因按业务阶段选模式首次提交前推荐默认的submit避免过早打扰提交后用change可让用户边改边消错用blur则更克制适合异步校验较多的场景字段级返回字符串、表单级返回对象字段级onDynamic返回错误字符串表单级onDynamic返回按字段名组织的错误对象读取时分别使用field.state.meta.errorMap.onDynamic与form.state.errorMap.onDynamic?.字段名异步校验记得防抖涉及服务端检查如用户名查重时用onDynamicAsyncDebounceMs控制请求频率可与常规校验混用revalidateLogic不会丢弃常规校验器onChange/onBlur等仍会按原有规则执行错误分别存储、互不干扰。需要进一步阅读时可查看 动态校验官方指南、revalidateLogic 函数参考、FormValidators 接口以及 Vue 适配层 useForm.tsx 与 useField.tsx 中validationLogic的透传方式。【免费下载链接】form Headless, performant, and type-safe form state management for TS/JS, React, Vue, Angular, Solid, and Lit.项目地址: https://gitcode.com/GitHub_Trending/form/form创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表