如何避免Vee Validate v4异步验证触发过于频繁?
解决Vee Validate v4中邮箱异步验证重复请求的问题
我正在开发一个注册表单,需要通过HTTP请求检查邮箱地址是否已被其他用户使用,并根据返回结果提示警告。当前使用Vue3和Vee Validate v4框架,但存在一个问题:每在任意字段输入内容时,整个表单都会触发验证,导致大量请求产生。我希望仅在邮箱字段失去焦点时才发起该HTTP请求。尝试使用useField/useForms替代Form组件,但未能妥善解决问题。
示例代码
<script setup lang="ts"> import { Field, Form, ErrorMessage } from 'vee-validate'; import * as yup from 'yup'; let call = 0; const schema = yup.object({ name: yup.string().required().min(8), email: yup .string() .required() .email() .test( 'isEmailUnique', 'This email is already taken', async function (value) { return new Promise((resolve) => { console.log(call++); setTimeout(() => { resolve(value === 'test@example.com'); }, 100); }); } ), }); function onSubmit(values) { alert(JSON.stringify(values, null, 2)); } </script> <template> <div> <h1>vee-validate Async Validation</h1> <Form @submit="onSubmit" :validation-schema="schema"> <label for="name">Name</label> <Field id="name" name="name" type="text" /> <ErrorMessage name="name" /> <label for="email">Email</label> <Field id="email" name="email" type="email" /> <ErrorMessage name="email" /> <button type="submit">Submit</button> </Form> </div> </template>
解决思路
1. 给邮箱字段单独设置验证触发时机
Vee Validate默认会在输入时触发验证,你可以给邮箱字段指定仅在blur(失焦)时触发验证,其他字段保持默认逻辑。只需给邮箱的Field组件添加validation-triggers属性:
<Field id="email" name="email" type="email" :validation-triggers="['blur']" />
这样只有当邮箱字段失去焦点时,才会触发它的所有验证规则(包括异步的唯一性检查),其他字段输入时不会触发邮箱的异步请求。
2. 拆分验证规则,分离异步逻辑
把邮箱的异步验证从全局yup schema中抽出来,单独通过useField控制验证时机,避免全局表单验证触发异步请求。示例代码如下:
<script setup lang="ts"> import { Field, Form, ErrorMessage, useField } from 'vee-validate'; import * as yup from 'yup'; let call = 0; // 基础表单验证规则(不含邮箱异步验证) const schema = yup.object({ name: yup.string().required().min(8), email: yup.string().required().email(), }); // 单独处理邮箱字段的状态与验证 const { value: emailValue, errorMessage: emailAsyncError, validate } = useField('email'); // 邮箱失焦时触发异步验证 const handleEmailBlur = async () => { if (!emailValue.value) return; // 模拟HTTP请求检查邮箱唯一性 const isUnique = await new Promise((resolve) => { console.log(call++); setTimeout(() => { resolve(emailValue.value !== 'test@example.com'); }, 100); }); // 根据结果手动设置验证状态 if (!isUnique) { validate(false, { isEmailUnique: 'This email is already taken' }); } else { validate(true); } }; const onSubmit = async (values) => { // 提交前再次验证邮箱唯一性,防止前端状态被篡改 const isUnique = await new Promise((resolve) => { setTimeout(() => { resolve(values.email !== 'test@example.com'); }, 100); }); if (!isUnique) { validate(false, { isEmailUnique: 'This email is already taken' }); return; } alert(JSON.stringify(values, null, 2)); }; </script> <template> <div> <h1>vee-validate Async Validation</h1> <Form @submit="onSubmit" :validation-schema="schema"> <label for="name">Name</label> <Field id="name" name="name" type="text" /> <ErrorMessage name="name" /> <label for="email">Email</label> <Field id="email" name="email" type="email" @blur="handleEmailBlur" /> <!-- 显示异步验证错误 --> <span v-if="emailAsyncError" class="error">{{ emailAsyncError }}</span> <ErrorMessage name="email" /> <button type="submit">Submit</button> </Form> </div> </template>
3. 使用useForm自定义表单验证流程
通过useForm获取表单实例,手动控制邮箱字段的验证时机,其他字段保持默认的输入验证逻辑:
<script setup lang="ts"> import { Field, ErrorMessage, useForm } from 'vee-validate'; import * as yup from 'yup'; let call = 0; const schema = yup.object({ name: yup.string().required().min(8), email: yup.string().required().email(), }); const { handleSubmit, validateField, setFieldError, clearFieldError } = useForm({ validationSchema: schema, }); // 邮箱失焦时触发异步验证 const handleEmailBlur = async () => { const { valid, value } = await validateField('email'); if (!valid || !value) return; // 模拟HTTP请求 const isUnique = await new Promise((resolve) => { console.log(call++); setTimeout(() => { resolve(value !== 'test@example.com'); }, 100); }); if (!isUnique) { setFieldError('email', 'This email is already taken'); } else { clearFieldError('email'); } }; const onSubmit = handleSubmit(async (values) => { // 提交前二次验证 const isUnique = await new Promise((resolve) => { setTimeout(() => { resolve(values.email !== 'test@example.com'); }, 100); }); if (!isUnique) { setFieldError('email', 'This email is already taken'); return; } alert(JSON.stringify(values, null, 2)); }); </script> <template> <div> <h1>vee-validate Async Validation</h1> <form @submit="onSubmit"> <label for="name">Name</label> <Field id="name" name="name" type="text" /> <ErrorMessage name="name" /> <label for="email">Email</label> <Field id="email" name="email" type="email" @blur="handleEmailBlur" /> <ErrorMessage name="email" /> <button type="submit">Submit</button> </form> </div> </template>
内容的提问来源于stack exchange,提问作者megadave
相关产品推荐
相关产品推荐

