Vue 3 表单验证组合式 API,提供类似 Ant Design Vue Form 的强大表单验证功能

164 阅读4分钟

Vue Form Validator

🚀 Vue 3 表单验证组合式 API,提供类似 Ant Design Vue Form 的强大表单验证功能

[npm 仓库](www.npmjs.com/package/@ea…)

✨ 特性

  • 🎯 Vue 3 优化: 基于 Composition API 构建,完全支持响应式
  • 🔧 TypeScript 支持: 完整的类型定义,享受类型安全和智能提示
  • 🌊 灵活验证: 支持单字段、多字段、整表单验证
  • 🏗️ 深度规则: 支持嵌套对象和数组的复杂验证场景
  • 防抖优化: 可配置的防抖验证,提升用户体验
  • 🧪 开发工具: 内置性能监控和规则验证工具
  • 📦 轻量无依赖: 基于 async-validator,体积小巧
  • 🔄 兼容 Element Plus: 无缝集成 el-form-item 组件

解决 Element Plus 表单验证的痛点

Element Plus 的表单验证虽然功能完善,但在现代 Vue 3 开发中存在一些局限性:

🔗 组件耦合度高
<!-- Element Plus 方式:必须依赖 el-form 和 el-form-item -->
<el-form :model="form" :rules="rules" ref="formRef">
  <el-form-item label="用户名" prop="username">
    <el-input v-model="form.username" />
  </el-form-item>
</el-form>

<!-- Vue Form Validator:解耦组件依赖 -->
<el-form-item label="用户名" v-bind="validationState.username">
  <el-input v-model="model.username" />
</el-form-item>
📱 Composition API 支持有限
// Element Plus:需要通过 ref 获取表单实例
const formRef = ref()
const validate = () => formRef.value.validate()

// Vue Form Validator:纯 Composition API 体验
const { validate } = useForm(model, rules)
await validate() // 直接调用
🌊 深度验证困难
// Element Plus:嵌套验证配置复杂
const rules = {
  'user.profile.name': [{ required: true }], // 不够直观
}

// Vue Form Validator:原生深度验证支持
const rules = {
  'user.profile': {
    type: 'object',
    fields: {
      name: { required: true, message: '请输入姓名' },
      contacts: {
        type: 'array',
        defaultField: {
          /* 数组元素验证规则 */
        },
      },
    },
  },
}

🚀 Vue Form Validator 的技术优势

1. 智能响应式验证状态
// 验证状态自动响应式更新,无需手动管理
interface UseFormValidateInfo {
  required?: boolean // 自动检测是否必填
  validateStatus?: 'error' | 'validating' | 'success' | ''
  error?: string // 错误信息
}
2. 高性能懒加载机制
  • 使用 Proxy 实现深度字段的懒加载
  • 只有在访问时才创建对应的验证规则和监听器
  • 智能内存管理,自动清理不需要的资源
3. 内置防抖优化
const { validationState } = useForm(model, rules, {
  debounceMs: 300, // 用户停止输入 300ms 后才验证
})
4. 完整的 TypeScript 支持
interface UserForm {
  username: string
  email: string
}

// 完整的类型推导和约束
const { validationState } = useForm<UserForm>(model, rules)
// validationState.username ✅ 类型安全
// validationState.nonexistent ❌ TypeScript 错误

📊 性能对比

特性Vue Form ValidatorElement Plus Form
响应式更新🟢 精确更新,只更新变化的字段🟡 可能触发整个表单重新渲染
内存使用🟢 懒加载 + 自动清理🟡 需要手动管理
验证频率🟢 内置防抖,可配置🟡 需要手动实现
深度验证🟢 原生支持,性能优化🔴 复杂场景性能差
TypeScript🟢 完整类型推导🟡 基础类型支持
组合式 API🟢 专为 Composition API 设计🟡 混合支持

📦 安装

# npm
npm install @easily-js/vue-form-validator async-validator

# yarn
yarn add @easily-js/vue-form-validator async-validator

# pnpm
pnpm add @easily-js/vue-form-validator async-validator

🚀 快速开始

基础用法

<template>
  <el-form-item label="用户名" v-bind="validationState.username">
    <el-input v-model="model.username" placeholder="请输入用户名" />
  </el-form-item>

  <el-form-item label="邮箱" v-bind="validationState.email">
    <el-input v-model="model.email" placeholder="请输入邮箱" />
  </el-form-item>

  <el-form-item label="年龄" v-bind="validationState.age">
    <el-input-number v-model="model.age" />
  </el-form-item>
  <el-form-item>
    <el-button type="primary" @click="handleSubmit">提交</el-button>
    <el-button @click="resetFields">重置</el-button>
    <el-button @click="clearValidate">清除验证</el-button>
  </el-form-item>
</template>

<script setup lang="ts">
import { reactive } from 'vue'
import { useForm, type UseFormRules } from 'vue-form-validator'

// 定义表单数据类型
interface UserForm {
  username: string
  email: string
  age: number
}

// 定义验证规则
const rules: UseFormRules<UserForm> = {
  username: [
    { required: true, message: '请输入用户名' },
    { min: 3, max: 20, message: '用户名长度为 3-20 个字符' },
  ],
  email: [
    { required: true, message: '请输入邮箱地址' },
    { type: 'email', message: '请输入正确的邮箱格式' },
  ],
  age: [
    { required: true, message: '请输入年龄' },
    { type: 'number', min: 18, max: 120, message: '年龄必须在 18-120 之间' },
  ],
}

// 初始化表单
const model = reactive<UserForm>({
  username: '',
  email: '',
  age: 18,
})

// 使用 useForm
const { validationState, validate, validateField, resetFields, clearValidate } =
  useForm(model, rules)

// 提交处理
async function handleSubmit() {
  try {
    await validate()
    console.log('表单验证通过', model)
    // 处理提交逻辑
  } catch (errorInfo) {
    console.log('验证失败', errorInfo)
  }
}
</script>

高级用法

1. 防抖验证
<script setup lang="ts">
// 配置 300ms 防抖延迟
const { validationState, validate } = useForm(model, rules, {
  debounceMs: 300, // 用户停止输入 300ms 后再触发验证
})
</script>
2. 深度规则验证
<script setup lang="ts">
import { reactive } from 'vue'

// 嵌套对象表单
const model = reactive({
  user: {
    profile: {
      name: '',
      contact: {
        email: '',
        phone: '',
      },
    },
    preferences: {
      theme: 'light',
      notifications: true,
    },
  },
})

// 深度规则
const rules = {
  'user.profile.name': [{ required: true, message: '请输入姓名' }],
  'user.profile.contact.email': [
    { required: true, message: '请输入邮箱' },
    { type: 'email', message: '邮箱格式不正确' },
  ],
  'user.profile.contact.phone': [
    { required: true, message: '请输入手机号' },
    { pattern: /^1[3-9]\d{9}$/, message: '手机号格式不正确' },
  ],
}

const { validationState } = useForm(model, rules, {
  deepRule: true, // 启用深度规则
})
</script>

<template>
  <!-- 访问嵌套字段的验证状态 -->
  <el-form-item label="姓名" v-bind="validationState['user.profile.name']">
    <el-input v-model="model.user.profile.name" />
  </el-form-item>

  <el-form-item
    label="邮箱"
    v-bind="validationState['user.profile.contact.email']"
  >
    <el-input v-model="model.user.profile.contact.email" />
  </el-form-item>
</template>
3. 数组验证
<script setup lang="ts">
const model = reactive({
  tags: [''],
  users: [{ name: '', email: '' }],
})

const rules = {
  tags: {
    type: 'array',
    required: true,
    defaultField: { type: 'string', required: true, message: '标签不能为空' },
  },
  users: {
    type: 'array',
    required: true,
    defaultField: {
      type: 'object',
      fields: {
        name: { required: true, message: '请输入姓名' },
        email: { type: 'email', required: true, message: '请输入正确的邮箱' },
      },
    },
  },
}
</script>
4. 自定义验证器
<script setup lang="ts">
// 异步验证器示例
const checkUsernameAvailability = async (rule: any, value: string) => {
  if (!value) return Promise.reject('请输入用户名')

  // 模拟 API 调用
  const response = await fetch(`/api/check-username?name=${value}`)
  const { available } = await response.json()

  if (!available) {
    return Promise.reject('用户名已被占用')
  }
}

const rules = {
  username: [
    { required: true, message: '请输入用户名' },
    { validator: checkUsernameAvailability, trigger: 'blur' },
  ],
}
</script>