Tailwind CSSTailwind CSS
Home
  • Tailwind CSS 书籍目录
  • Vue 3 开发实战指南
  • React 和 Next.js 学习
  • TypeScript
  • React开发框架书籍大纲
  • Shadcn学习大纲
  • Swift 编程语言:从入门到进阶
  • SwiftUI 学习指南
  • 函数式编程大纲
  • Swift 异步编程语言
  • Swift 协议化编程
  • SwiftUI MVVM 开发模式
  • SwiftUI 图表开发书籍
  • SwiftData
  • ArkTS编程语言:从入门到精通
  • 仓颉编程语言:从入门到精通
  • 鸿蒙手机客户端开发实战
  • WPF书籍
  • C#开发书籍
learn
  • Java编程语言
  • Kotlin 编程入门与实战
  • /python/outline.html
  • AI Agent
  • MCP (Model Context Protocol) 应用指南
  • 深度学习
  • 深度学习
  • 强化学习: 理论与实践
  • 扩散模型书籍
  • Agentic AI for Everyone
langchain
Home
  • Tailwind CSS 书籍目录
  • Vue 3 开发实战指南
  • React 和 Next.js 学习
  • TypeScript
  • React开发框架书籍大纲
  • Shadcn学习大纲
  • Swift 编程语言:从入门到进阶
  • SwiftUI 学习指南
  • 函数式编程大纲
  • Swift 异步编程语言
  • Swift 协议化编程
  • SwiftUI MVVM 开发模式
  • SwiftUI 图表开发书籍
  • SwiftData
  • ArkTS编程语言:从入门到精通
  • 仓颉编程语言:从入门到精通
  • 鸿蒙手机客户端开发实战
  • WPF书籍
  • C#开发书籍
learn
  • Java编程语言
  • Kotlin 编程入门与实战
  • /python/outline.html
  • AI Agent
  • MCP (Model Context Protocol) 应用指南
  • 深度学习
  • 深度学习
  • 强化学习: 理论与实践
  • 扩散模型书籍
  • Agentic AI for Everyone
langchain
  • 实战案例:构建一个可复用的表单验证 Hook

实战案例:构建一个可复用的表单验证 Hook

在 Vue 3 开发中,表单验证是常见需求,通常涉及输入校验、错误提示和提交处理。借助 Composition API 的自定义 Hook,我们可以封装一个通用的表单验证逻辑,使其在多个组件中复用。本节将通过实战案例,设计并实现一个可复用的表单验证 Hook,展示如何结合响应式 API 和生命周期钩子解决实际问题。

需求分析

我们需要一个表单验证 Hook,具备以下功能:

  1. 支持多字段:管理多个输入字段的状态和验证规则。
  2. 动态校验:实时验证输入并显示错误。
  3. 提交处理:提供表单提交方法,验证通过后执行操作。
  4. 可配置:允许自定义规则和错误消息。
  5. 复用性:适用于不同表单场景。

设计与实现

Hook 定义

创建一个 useFormValidation Hook,放在 src/composables/useFormValidation.js:

import { reactive, ref, watch } from 'vue';

export function useFormValidation(initialFields, rules) {
  // 表单字段状态
  const form = reactive({ ...initialFields });
  
  // 错误信息
  const errors = reactive({});
  
  // 是否验证通过
  const isValid = ref(false);

  // 验证单个字段
  const validateField = (field) => {
    const value = form[field];
    const fieldRules = rules[field] || [];
    
    // 清空该字段的错误
    errors[field] = '';
    
    // 执行所有规则
    for (const rule of fieldRules) {
      const result = rule.validator(value);
      if (result !== true) {
        errors[field] = rule.message;
        return;
      }
    }
  };

  // 验证整个表单
  const validateForm = () => {
    Object.keys(form).forEach(field => validateField(field));
    isValid.value = Object.values(errors).every(error => !error);
    return isValid.value;
  };

  // 实时监听字段变化
  Object.keys(form).forEach(field => {
    watch(() => form[field], () => {
      validateField(field);
      validateForm(); // 更新整体状态
    });
  });

  // 重置表单
  const resetForm = () => {
    Object.assign(form, initialFields);
    Object.keys(errors).forEach(field => (errors[field] = ''));
    isValid.value = false;
  };

  // 提交处理
  const submit = (onSuccess) => {
    if (validateForm()) {
      onSuccess(form);
    }
  };

  // 初始验证
  validateForm();

  return {
    form,
    errors,
    isValid,
    validateField,
    validateForm,
    resetForm,
    submit
  };
}

参数说明

  • initialFields:初始表单字段对象(如 { name: '', email: '' })。
  • rules:验证规则对象,格式为:
    {
      field: [
        { validator: value => condition, message: '错误消息' }
      ]
    }
    

使用示例

简单登录表单

<template>
  <div>
    <h2>登录</h2>
    <div>
      <label>用户名</label>
      <input v-model="form.name" />
      <span v-if="errors.name" class="error">{{ errors.name }}</span>
    </div>
    <div>
      <label>密码</label>
      <input v-model="form.password" type="password" />
      <span v-if="errors.password" class="error">{{ errors.password }}</span>
    </div>
    <button :disabled="!isValid" @click="submit(handleSubmit)">提交</button>
    <button @click="resetForm">重置</button>
  </div>
</template>

<script>
import { useFormValidation } from '@/composables/useFormValidation';

export default {
  setup() {
    const initialFields = {
      name: '',
      password: ''
    };

    const rules = {
      name: [
        { validator: value => value.length > 0, message: '用户名不能为空' },
        { validator: value => value.length >= 3, message: '用户名至少3个字符' }
      ],
      password: [
        { validator: value => value.length > 0, message: '密码不能为空' },
        { validator: value => value.length >= 6, message: '密码至少6个字符' }
      ]
    };

    const {
      form,
      errors,
      isValid,
      resetForm,
      submit
    } = useFormValidation(initialFields, rules);

    const handleSubmit = (formData) => {
      console.log('提交成功:', formData);
    };

    return { form, errors, isValid, resetForm, submit };
  }
};
</script>

<style scoped>
.error {
  color: red;
  font-size: 12px;
}
div {
  margin: 10px 0;
}
input {
  display: block;
  margin: 5px 0;
}
</style>
  • 效果:
    • 输入时实时验证,显示错误消息。
    • 所有字段有效时,提交按钮启用,点击打印表单数据。
    • 重置按钮恢复初始状态。

高级用法

自定义复杂规则

支持正则表达式或异步验证:

const rules = {
  email: [
    { validator: value => value.length > 0, message: '邮箱不能为空' },
    { validator: value => /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(value), message: '邮箱格式错误' }
  ],
  username: [
    {
      validator: async (value) => {
        const response = await fetch(`/api/check-username?value=${value}`);
        return response.ok;
      },
      message: '用户名已存在'
    }
  ]
};
  • 注意:异步验证需配合 async/await,可能需要额外的加载状态管理。

与生命周期结合

在组件挂载时预填数据:

<script>
import { useFormValidation } from '@/composables/useFormValidation';
import { onMounted } from 'vue';

export default {
  setup() {
    const initialFields = { name: '', email: '' };
    const rules = { /* ... */ };
    
    const { form, errors, isValid } = useFormValidation(initialFields, rules);

    onMounted(async () => {
      const response = await fetch('/api/user');
      const user = await response.json();
      Object.assign(form, user); // 预填数据
    });

    return { form, errors, isValid };
  }
};
</script>

设计亮点

  1. 响应式管理:
    • 使用 reactive 管理表单和错误状态,确保实时更新。
  2. 灵活性:
    • 支持任意字段和规则,适应不同表单需求。
  3. 状态完整:
    • 提供 isValid 和 errors,便于控制 UI。
  4. 复用性:
    • 可在登录、注册或其他表单场景直接使用。

注意事项

  1. 性能优化:
    • 避免过于复杂的规则或频繁触发的 watch,可添加防抖:
      import { debounce } from 'lodash';
      watch(() => form[field], debounce(() => validateField(field), 300));
      
  2. 规则顺序:
    • 规则按数组顺序执行,优先级高的放在前面。
  3. 错误清理:
    • 确保 errors 对象的键与字段一致,避免冗余。

扩展:类型支持

为 Hook 添加 TypeScript 类型:

interface FormField {
  [key: string]: string | number | boolean;
}

interface ValidationRule {
  validator: (value: any) => boolean | Promise<boolean>;
  message: string;
}

export function useFormValidation<T extends FormField>(
  initialFields: T,
  rules: { [K in keyof T]?: ValidationRule[] }
) {
  const form = reactive<T>({ ...initialFields });
  const errors = reactive<Partial<Record<keyof T, string>>>({});
  // ... 其余代码 ...
}

使用时:

const initialFields = { name: '', email: '' };
const rules = {
  name: [{ validator: v => v.length > 0, message: '必填' }],
  email: [{ validator: v => v.includes('@'), message: '格式错误' }]
};
const { form, errors } = useFormValidation(initialFields, rules);

总结

通过 useFormValidation Hook,我们实现了一个功能强大且可复用的表单验证方案。它结合 reactive、watch 和自定义逻辑,满足实时校验和提交需求。掌握此案例后,你可以根据项目需要扩展规则或功能,轻松应对各种表单场景。本章结束,你已深入 Composition API 的高级应用,下一章将探讨组件化开发的更多技巧!

Last Updated:: 2/24/25, 10:33 AM