一、为什么表单验证总让人头疼

每次做表单功能时,最烦人的就是验证逻辑。手机号要11位还要特定开头、密码必须包含大小写、邮箱要带@符号...这些规则写起来特别琐碎。更麻烦的是,错误提示要实时显示,提交时还要整体校验,代码很容易变成一锅粥。

传统做法是用一堆if-else,比如这样:

// 技术栈:Vue 3 + Vuelidate
if (!formData.username) {
  errors.push('用户名不能为空')
} else if (formData.username.length < 3) {
  errors.push('用户名太短')
}
// 后面还有十几行类似的判断...

这种代码维护起来特别痛苦,每次改需求都要在面条代码里找半天。今天我们介绍的主角Vuelidate,就是专门来解决这个痛点的。

二、Vuelidate初体验

Vuelidate是个轻量级验证库,它的核心思想是"声明式验证"。不用写具体判断逻辑,只要声明字段应该满足什么条件就行。先看个最简单的例子:

// 技术栈:Vue 3 + Vuelidate
import { required, minLength } from '@vuelidate/validators'

const rules = {
  username: {
    required,  // 必填
    minLength: minLength(3) // 最小3个字符
  }
}

然后在模板里可以这样用:

<input v-model="username" />
<div v-if="v$.username.$error">
  <span v-if="v$.username.required.$invalid">必填项</span>
  <span v-if="v$.username.minLength.$invalid">至少3个字符</span>
</div>

是不是比if-else清爽多了?所有验证规则都像积木一样可以自由组合。

三、实战复杂表单验证

来看个更完整的用户注册表单例子,包含这些验证需求:

  1. 用户名:必填,3-20位
  2. 密码:必须包含大小写和数字
  3. 手机号:符合中国大陆格式
  4. 年龄:18岁以上
// 技术栈:Vue 3 + Vuelidate
import { 
  required, 
  minLength, 
  maxLength,
  helpers
} from '@vuelidate/validators'

// 自定义密码验证
const containsUppercase = value => /[A-Z]/.test(value)
const containsLowercase = value => /[a-z]/.test(value)
const containsNumber = value => /[0-9]/.test(value)

// 自定义手机号验证
const isPhone = value => /^1[3-9]\d{9}$/.test(value)

const rules = {
  username: {
    required,
    minLength: minLength(3),
    maxLength: maxLength(20)
  },
  password: {
    required,
    minLength: minLength(8),
    containsUppercase: helpers.withMessage(
      '必须包含大写字母',
      containsUppercase
    ),
    containsLowercase: helpers.withMessage(
      '必须包含小写字母',
      containsLowercase
    ),
    containsNumber: helpers.withMessage(
      '必须包含数字',
      containsNumber
    )
  },
  phone: {
    required,
    isPhone: helpers.withMessage(
      '请输入正确的手机号',
      isPhone
    )
  },
  age: {
    required,
    minValue: helpers.withMessage(
      '必须年满18岁',
      value => value >= 18
    )
  }
}

模板部分这样展示错误信息:

<!-- 技术栈:Vue 3 + Vuelidate -->
<form @submit.prevent="submit">
  <!-- 用户名输入 -->
  <input v-model="form.username" />
  <div class="errors">
    <div v-for="error of v$.username.$errors" :key="error.$uid">
      {{ error.$message }}
    </div>
  </div>

  <!-- 其他字段类似... -->
  
  <button :disabled="v$.$invalid">提交</button>
</form>

四、高级技巧与陷阱规避

1. 异步验证处理

比如验证用户名是否已存在:

// 技术栈:Vue 3 + Vuelidate
async function isUnique(value) {
  const res = await api.checkUsername(value)
  return res.available
}

const rules = {
  username: {
    // 其他规则...
    unique: helpers.withAsync(isUnique)
  }
}

2. 表单组验证

当有地址这类复合字段时:

const rules = {
  address: {
    required,
    street: { required },
    city: { required },
    zipCode: {
      required,
      numeric: helpers.withMessage('必须是数字', value => /^\d+$/.test(value))
    }
  }
}

3. 常见坑点

  • 动态表单字段需要用computed包装规则
  • 复杂自定义验证记得用helpers.withMessage添加友好提示
  • 提交前手动触发全部验证:await v$.value.$validate()

五、为什么选择Vuelidate

优势:

  1. 零依赖:不依赖其他库,体积仅3KB
  2. 组合式API:完美适配Vue 3的composition API
  3. 灵活扩展:可以轻松添加自定义验证器
  4. 清晰错误提示:内置错误消息定制功能

对比其他方案:

  • 比原生验证更强大
  • 比VeeValidate更轻量
  • 比手动写验证逻辑更可维护

适用场景:

  • 中后台管理系统表单
  • 需要复杂验证规则的表单
  • 需要复用验证逻辑的项目

六、最佳实践建议

  1. 把常用验证规则提取为共享模块
  2. 为业务字段创建语义化的验证器,如isEmployeeId
  3. 在大型项目中使用TypeScript增强类型提示
  4. 配合i18n实现多语言错误消息
// 技术栈:Vue 3 + Vuelidate + TypeScript
interface FormData {
  username: string
  password: string
  // 其他字段...
}

const rules = {
  username: {
    required: required as Rule<FormData['username']>
    // 其他规则...
  }
  // 其他字段规则...
}

七、总结

Vuelidate就像表单验证的瑞士军刀,用声明式的方法把繁琐的验证逻辑变得优雅可维护。特别是对于复杂的业务表单,它能帮你:

  • 减少70%以上的验证代码
  • 提升代码可读性
  • 轻松应对需求变更
  • 统一全站的验证体验

下次遇到表单验证需求时,不妨试试这个方案,相信你会爱上这种清爽的感觉!