从前端小白到表单验证高手的进阶之路与实战经验分享

Mc.佳沫 前端 阅读 965
赞 18 收藏
二维码
手机扫码查看
反馈

我的写法,亲测靠谱

表单验证这事儿,我折腾了不下百遍。现在回头看,最靠谱的还是基于约束和反馈的设计。先说说我现在的做法吧:

从前端小白到表单验证高手的进阶之路与实战经验分享

function validateForm(formData) {
  const errors = {};
  
  if (!formData.username.trim()) {
    errors.username = "用户名不能为空";
  } else if (formData.username.length < 3) {
    errors.username = "用户名至少3个字符";
  }

  if (!/^[^@s]+@[^@s]+.[^@s]+$/.test(formData.email)) {
    errors.email = "邮箱格式不正确";
  }

  if (formData.password.length < 6) {
    errors.password = "密码至少6位";
  }

  return errors;
}

这样写的几个好处:首先,校验逻辑集中管理,后续维护方便;其次,每个字段的错误信息都单独返回,方便在界面上做针对性提示;最后,trim()这个小细节很关键,用户复制粘贴时经常带空格。

调用的时候也很简单:

const formData = {
  username: "  ",
  email: "test#example",
  password: "123"
};

const errors = validateForm(formData);
if (Object.keys(errors).length > 0) {
  console.log(errors); // 显示具体错误
} else {
  submitForm(formData);
}

这几种错误写法,别再踩坑了

见过太多反面教材,这里说几个常见的坑。首先是这种写法:

if (!username) alert("用户名不能为空");
if (!email) alert("邮箱不能为空");
// ...

为什么说它不好?一是用户体验差,一个接一个弹窗烦死;二是代码耦合度高,改个提示方式得改一堆地方;三是没法统一处理错误。

还有一种更离谱的:

onSubmit() {
  try {
    if (!this.state.username) throw new Error("用户名不能为空");
    if (!this.state.email) throw new Error("邮箱不能为空");
    // ...
  } catch (e) {
    alert(e.message);
  }
}

看着挺聪明?实际问题很大。异常处理是用来处理程序错误的,不是用来控制业务流程的。这样做不仅语义不清,而且调试起来特别痛苦。

最后一个常见错误是过度依赖HTML5自带的验证:

<input type="email" required />

虽然能省事,但有几个问题:样式定制性差、错误提示不够灵活、兼容性问题多。比如iOS上某些版本对required的支持就有问题。

实际项目中的坑

说说我在真实项目里遇到的一些特殊情况。有次做企业管理系统,客户要求同一个页面同时支持中英文提示,而且还得根据浏览器语言自动切换。当时我是这么处理的:

const messages = {
  zh: {
    required: "不能为空",
    email: "邮箱格式不正确"
  },
  en: {
    required: "is required",
    email: "invalid email format"
  }
};

const lang = navigator.language.startsWith("zh") ? "zh" : "en";

function getErrorMessage(type) {
  return messages[lang][type];
}

这样做的好处是扩展性强,后面加新语言很方便。不过也踩了个坑:有些字段的提示需要包含动态内容,比如”密码长度不能少于{min}位”。后来改成模板字符串才解决。

还有个大坑是异步验证。比如用户名是否已存在这种场景:

async function checkUsername(username) {
  const res = await fetch(https://jztheme.com/api/check?username=${username});
  return res.ok;
}

function validateForm(formData) {
  return new Promise(async (resolve, reject) => {
    const errors = {};
    
    if (!await checkUsername(formData.username)) {
      errors.username = "用户名已存在";
    }
    
    resolve(errors);
  });
}

这里要注意,validateForm必须返回Promise,否则没法等异步结果。我当时就在这卡了半天,老是拿不到最终的验证结果。

其他值得注意的小细节

说几个容易被忽视但很重要的点:

  • 移动端输入框获得焦点时,最好自动滚动到合适位置,不然键盘会挡住输入框
  • 日期选择器一定要考虑时区问题,后端存的是UTC时间,前端显示要转换
  • 数字输入框记得限制最大最小值,防止用户输入超范围的值

这些小问题看似不起眼,但处理不好用户体验会很差。比如日期时区问题,我就遇到过用户投诉说:”我明明选的是今天,怎么显示是昨天?” 后来发现是因为没处理好时区转换。

关于性能优化也有个小技巧:对于复杂的表单,不要每次输入都做完整校验。可以用debounce延迟校验,或者只校验当前修改的字段:

let timer;
function onInputChange(field, value) {
  clearTimeout(timer);
  timer = setTimeout(() => {
    validateField(field, value);
  }, 300);
}

这样既能保证实时反馈,又不会造成性能浪费。

以上是我总结的最佳实践

表单验证这块儿水还挺深,写了这么多年代码,感觉最重要的是:一要注重用户体验,二要考虑各种边界情况,三要保持代码的可维护性。

这些经验都是踩过无数坑总结出来的,像那些反人类的弹窗提示、莫名其妙的验证规则、让人抓狂的异步校验,都折磨过我好久。希望能帮大家少走弯路。

当然,肯定还有更好的方案,欢迎评论区交流。最近也在研究一些新的验证库,后续有收获再来分享。

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

暂无评论