自定义校验规则在前端表单中的实战应用与优化技巧
自定义校验,到底该用谁?
最近在重构一个老表单系统,里面一堆校验规则写得又臭又长。我实在忍不了,决定重新梳理一下自定义校验的方案。市面上常见的无非就三种:原生 HTML5 + JS 手搓、用 validator.js 这类工具库、或者直接上 Formik / React Hook Form(Vue 里就是 VeeValidate / Element Plus 自带校验)。折腾了几天,踩了几个坑,今天就来唠唠我的真实体验。
手写校验:灵活但容易翻车
先说最原始的——纯手写。很多人觉得这太 low,但其实小项目里特别快。比如一个简单的手机号校验:
function validatePhone(phone) {
const reg = /^1[3-9]d{9}$/;
return reg.test(phone) ? '' : '手机号格式不正确';
}
绑定到 input 的 onBlur 或 onChange 里,完事。这种写法最大的好处是完全可控,没有额外依赖,逻辑一目了然。
但问题也来了:一旦表单字段一多,校验规则互相依赖(比如“密码”和“确认密码”要一致),代码立马变得又臭又长。更别说还要处理异步校验(比如查用户名是否已注册)——你得自己管理 loading 状态、防抖、错误清除时机……我之前在一个项目里就这么干,结果改个校验规则要动三处地方,差点被同事骂死。
所以我的结论是:简单表单可以手写,复杂点的别硬扛。
工具库派:validator.js 真香?
然后我试了 validator.js,这个库轻量(不到 10KB),API 也简单:
import validator from 'validator';
if (!validator.isEmail(email)) {
setError('邮箱格式不对');
}
它内置了几十种常用校验,像邮箱、URL、身份证、银行卡号都有,省了不少正则功夫。而且支持自定义扩展,比如加个“不能包含特殊字符”的规则:
validator.extend('noSpecialChars', (str) => {
return !/[^a-zA-Z0-9]/.test(str);
});
看起来很美好,对吧?但实际用起来发现两个痛点:
- 它只负责“判断”,不负责“状态管理”。你还是得自己存 error、控制显示隐藏、处理异步。
- 和 UI 框架(比如 Ant Design、Element Plus)的校验 API 对不上,经常要包一层适配器。
折腾半天,发现省下的那点正则时间,全花在 glue code 上了。除非你项目特别轻量,又不想引入大框架,否则我不推荐单独用它。
表单库派:React Hook Form 是我的最终选择
最后我试了 React Hook Form(RHF),直接打开新世界大门。它的自定义校验写法清爽到离谱:
const { register, handleSubmit, formState: { errors } } = useForm();
// 自定义校验函数
const validatePassword = (value) => {
if (value.length < 6) return '至少6位';
if (!/d/.test(value)) return '需包含数字';
return true; // 注意:必须返回 true 表示通过
};
return (
<input
{...register('password', {
validate: validatePassword
})}
/>
{errors.password && <span>{errors.password.message}</span>}
);
更狠的是异步校验,一行搞定:
validate: async (value) => {
const res = await fetch(https://jztheme.com/api/check-username?name=${value});
const data = await res.json();
return data.available ? true : '用户名已存在';
}
RHF 自动处理 loading、防抖、错误重置,连依赖字段(比如 confirm password 要和 password 一致)都支持:
validate: (value) => value === getValues('password') || '两次密码不一致'
我最喜欢的是它的性能——默认只校验 touched 的字段,不会一上来就红一片。而且和主流 UI 库(MUI、AntD、Chakra)集成文档齐全,基本 copy-paste 就能跑。
当然,它也有缺点:学习成本比手写高一点,而且重度依赖 React(Vue 选手请看 VeeValidate,理念类似)。但一旦上手,真的回不去了。现在我新项目一律 RHF,老项目也在慢慢迁移。
Vue 选手怎么办?
如果你用 Vue,别慌。VeeValidate 3+ 的 API 设计思路和 RHF 很像,自定义校验也是一等公民:
import { useField } from 'vee-validate';
const { value, errorMessage } = useField('email', (value) => {
if (!value) return '必填';
if (!/^S+@S+.S+$/.test(value)) return '邮箱格式不对';
return true;
});
Element Plus 自带的校验规则虽然也能用,但写法啰嗦,异步支持弱,我一般只在超小型项目里将就用。VeeValidate 才是正道。
我的选型逻辑
总结一下我的偏好:
- 超简单表单(1-2 个字段):手写,别装了,快得很。
- 中等复杂度,无异步校验:可以考虑 validator.js + 自己封装状态,但要评估维护成本。
- 中大型项目,尤其有异步/交叉校验:直接上表单库。React 选 React Hook Form,Vue 选 VeeValidate。别犹豫,省下的时间够你喝三杯咖啡。
有人可能会说“表单库太重”,但 RHF 压缩后也就 10KB 左右,换来的是可维护性和开发效率的飞跃。我宁愿多加载 10KB,也不想半夜被校验 bug 叫醒。
踩坑提醒:这三点一定注意
最后分享几个血泪教训:
- 自定义校验函数必须同步返回 true/false/string。如果忘了 return true,RHF 会认为校验失败!我在这栽过两次。
- 异步校验记得加防抖,否则用户打字时疯狂请求接口。RHF 默认没防抖,得自己包一层
lodash.debounce。 - 交叉校验(比如 confirm password)一定要用
getValues()或类似 API 实时读取其他字段值,别用闭包缓存——值会 stale。
以上是我对自定义校验方案的完整对比和实战总结。React Hook Form 现在是我的主力武器,虽然前期要学点新概念,但长期看绝对值得。如果你有更好的方案或踩过别的坑,欢迎评论区交流!

暂无评论