标准答案

  1. 规则可以按字段组织,例如 required、minLength、pattern、validator。
  2. validateField 负责校验单个字段,validate 负责校验整个表单。
  3. 校验结果应包含是否通过和错误信息,方便 UI 展示。
  4. 异步校验要返回 Promise,例如用户名是否重复。
  5. 实际项目还要考虑触发时机、脏状态、提交中状态、错误清理和跨字段校验。

题目解析

表单校验器的重点不是写几个 if,而是把校验规则变成可配置、可复用、可组合的结构。这样业务字段增加时,不需要把校验逻辑散落在事件回调里。

同步校验和异步校验最好统一成 Promise 流程。这样 required、正则校验和远程唯一性校验都可以走同一个 validate 方法。

跨字段校验是常见边界,比如确认密码依赖密码字段,开始时间不能晚于结束时间。规则函数需要拿到当前字段值,也要能拿到整个表单值。

代码示例

下面的实现支持字段规则数组、同步校验和异步校验。

JavaScript
async function validateField(values, field, rules) {
  const value = values[field]

  for (const rule of rules) {
    if (rule.required && (value === undefined || value === null || value === '')) {
      return rule.message || '必填项不能为空'
    }

    if (rule.pattern && !rule.pattern.test(String(value))) {
      return rule.message || '格式不正确'
    }

    if (rule.validator) {
      const result = await rule.validator(value, values)
      if (result !== true) {
        return typeof result === 'string' ? result : rule.message
      }
    }
  }

  return ''
}

async function validate(values, schema) {
  const errors = {}

  for (const field of Object.keys(schema)) {
    const message = await validateField(values, field, schema[field])
    if (message) {
      errors[field] = message
    }
  }

  return {
    valid: Object.keys(errors).length === 0,
    errors
  }
}

常见误区

  • 校验逻辑散落在每个输入框事件里,无法复用和统一展示错误。
  • 只支持同步校验,遇到远程唯一性检查就要重写结构。
  • 跨字段规则拿不到完整表单值,确认密码、时间范围这类场景无法处理。

作者信息