表单校验在 Web 开发中是必不可少的一环。在用户提交表单之前进行有效性验证能够确保输入的数据合法有效。为了提高开发效率、减少重复工作以及提高项目的可维护性和可扩展性,设计一个高级通用的表单校验函数就显得尤为重要。

设计思路

设计高级通用的表单校验函数需要考虑以下几点:

  1. 收集表单数据:针对前端页面中的表单元素获取用户输入的数据。这个数据可能包括文本、数字、日期、时间等。一般建议采用类似 "id-value" 的格式进行获取,以减少页面元素的依赖。
  2. 定义校验规则:需要明确每个表单元素必须满足哪些要求。比如是否必填、长度限制、格式验证等。定义表单规则时,建议采用对象存储规则,其中每个表单元素对应一个验证器数组,每个验证器又包括一个验证函数和相应的错误提示信息。采用这种方式,你就可以轻松添加新的验证器,而不需要修改验证函数本身。
  3. 实现校验逻辑:在规则的基础上对表单数据进行逐一检查,判断数据是否符合规则。校验顺序是按照添加的顺序进行校验,前一个校验器出现错误,则不再进行后续校验,这样可以避免重复校验,同时还可以提高性能。
  4. 展示错误信息:在校验数据之后,需要把错误信息展示给用户。你可以选择把所有的错误信息进行一次性展示,也可以对每个表单元素单独进行提示。同时,对于提交的数据,你需要进行数据清洗处理,避免无效数据被提交。

代码实现

下面是一个高级通用表单校验函数的示例代码:

/**
 * 对单个表单项的值进行校验
 * @param {String} value 表单项的值
 * @param {Array} rules 表单校验规则数组,每个元素是一个包含 validator 和 message 两个属性的对象
 * @returns {String} 如果校验通过,返回 "",否则返回错误提示信息
 */
function validateItem(value, rules) {
  for (let i = 0; i < rules.length; i++) {
    const rule = rules[i];
    if (!rule.validator(value)) {
      return rule.message;
    }
  }
  return "";
}

/**
 * 对整个表单进行校验
 * @param {Object} formData 表单数据对象
 * @param {Object} ruleList 表单校验规则对象
 * @returns {Object} 返回一个包含错误信息的对象,如果所有校验规则都通过,则返回一个空对象
 */
function validateForm(formData, ruleList) {
  const errors = {};
  for (const itemName in ruleList) {
    if (ruleList.hasOwnProperty(itemName)) {
      const rules = ruleList[itemName];
      const value = formData[itemName];
      const errMsg = validateItem(value, rules);
      if (errMsg) {
        errors[itemName] = errMsg;
      }
    }
  }
  return errors;
}

/**
 * 表单提交事件处理函数
 * @param {Event} e 
 */
function handleSubmit(e) {
  e.preventDefault();
  // 获取表单数据
  const formData = {};
  for (const el of e.target.elements) {
    if (el.name) {
      formData[el.name] = el.value.trim();
    }
  }
  // 定义校验规则
  const ruleList = {
    "username": [
      { validator: (v) => !!v, message: "用户名不能为空" },
      { validator: (v) => /^[a-zA-Z0-9_]{4,16}$/.test(v) , message: "用户名必须是4-16位的字母、数字或下划线" },
    ],
    "password": [
      { validator: (v) => !!v, message: "密码不能为空" },
      { validator: (v) => /^[\w@!#^$?%^&*()+=|{}[\]:;'",.<>\/?\\~`-]{6,16}$/.test(v), message: "密码必须是6-16位且包含数字、字母和特殊字符" },
    ],
    "repassword": [
      { validator: (v) => !!v, message: "确认密码不能为空" },
      { validator: (v, formData) => v === formData.password, message: "两次密码输入不一致" },
    ],
    "email": [
      { validator: (v) => /^\w+@[a-z0-9]+\.[a-z]+$/i.test(v), message: "邮箱格式不正确" },
    ],
  };
  // 校验表单数据
  const errors = validateForm(formData, ruleList);
  if (Object.keys(errors).length) {
    // 显示错误信息
    // ...
    console.log(errors);
    return;
  }
  // 处理表单数据
  // ...
}

在上述代码中,我们将表单数据和校验规则都抽象为对象,相比数组形式的规则定义,对象形式更加直观易懂。其中,校验规则对象的每个属性名对应表单项的 name 属性,属性值是一个规则数组,每个数组元素是一个对象,包含 validator 和 message 两个属性,分别表示校验函数和错误提示信息。表单数据对象的属性名同样对应表单项的 name 属性,属性值是表单项的值。

在校验逻辑中,我们遍历校验规则对象,逐一获取规则数组,针对每个表单项的值,调用 validateItem 函数,如果校验未通过,则将错误信息存入一个对象,最终将错误信息对象返回。

在编写校验函数时,需要注意以下几点:

  1. 校验函数的返回值必须是布尔类型,符合规则返回 true,否则返回 false。
  2. 部分校验函数需要使用到 formData 传递的其他表单项的值,比如表单项 A 的校验规则依赖于表单项 B 的值,则可以定义函数参数为 (value, formData)。
  3. 在表单项数量较多时,编写规则对象和表单数据对象可能比较麻烦,此时可以借助一些表单校验库,比如 VeeValidate、Validator.js 等,它们提供了一些方便的方法来定义表单校验规则,可以大大减少开发难度。

总结

设计高级通用的表单校验函数需要考虑校验规则、校验逻辑、错误信息提示和数据清洗等因素,同时需要使用一些优秀的编程习惯和技巧,比如定义校验函数和规则对象,将表单数据和校验规则分离等。在编写校验函数时需要严格按照要求返回布尔类型,并且注意参数传递的顺序,方便于适配各种校验需求。

文章目录