自定义校验规则在前端表单中的实战应用与优化技巧

Des.倩云 组件 阅读 1,777
赞 14 收藏
二维码
手机扫码查看
反馈

自定义校验,到底该用谁?

最近在重构一个老表单系统,里面一堆校验规则写得又臭又长。我实在忍不了,决定重新梳理一下自定义校验的方案。市面上常见的无非就三种:原生 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 的 onBluronChange 里,完事。这种写法最大的好处是完全可控,没有额外依赖,逻辑一目了然。

但问题也来了:一旦表单字段一多,校验规则互相依赖(比如“密码”和“确认密码”要一致),代码立马变得又臭又长。更别说还要处理异步校验(比如查用户名是否已注册)——你得自己管理 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 叫醒。

踩坑提醒:这三点一定注意

最后分享几个血泪教训:

  1. 自定义校验函数必须同步返回 true/false/string。如果忘了 return true,RHF 会认为校验失败!我在这栽过两次。
  2. 异步校验记得加防抖,否则用户打字时疯狂请求接口。RHF 默认没防抖,得自己包一层 lodash.debounce
  3. 交叉校验(比如 confirm password)一定要用 getValues() 或类似 API 实时读取其他字段值,别用闭包缓存——值会 stale。

以上是我对自定义校验方案的完整对比和实战总结。React Hook Form 现在是我的主力武器,虽然前期要学点新概念,但长期看绝对值得。如果你有更好的方案或踩过别的坑,欢迎评论区交流!

本文章不代表JZTHEME立场,仅为作者个人观点 / 研究心得 / 经验分享,旨在交流探讨,供读者参考。
发表评论

暂无评论