抛弃form中的rule验证,利用原生js,来实现表单的验证

276 阅读3分钟

先上代码

<script setup>
import { reactive, computed } from "vue";

const formData = reactive({
  student: {
    name: "",
    gender: "",
    age: null,
    isValid: computed(() => {
      return (
        formData.student.name &&
        formData.student.gender &&
        formData.student.age >= 6 &&
        formData.student.age <= 25
      );
    }),
  },
  parent: {
    name: "",
    phone: "",
    isValid: computed(() => {
      return (
        formData.parent.name && /^1[3-9]\d{9}$/.test(formData.parent.phone)
      );
    }),
  },
});

const errors = reactive({
  student: {
    name: "",
    gender: "",
    age: "",
  },
  parent: {
    name: "",
    phone: "",
  },
});

const validateStudentName = () => {
  if (!formData.student.name) {
    errors.student.name = "姓名不能为空";
  } else if (formData.student.name.length < 2) {
    errors.student.name = "姓名至少2个字符";
  } else {
    errors.student.name = "";
  }
};

const validateStudentAge = () => {
  if (!formData.student.age) {
    errors.student.age = "年龄不能为空";
  } else if (formData.student.age < 6) {
    errors.student.age = "年龄不能小于6岁";
  } else if (formData.student.age > 25) {
    errors.student.age = "年龄不能大于25岁";
  } else {
    errors.student.age = "";
  }
};

const validateParentPhone = () => {
  if (!formData.parent.phone) {
    errors.parent.phone = "手机号不能为空";
  } else if (!/^1[3-9]\d{9}$/.test(formData.parent.phone)) {
    errors.parent.phone = "请输入有效的手机号";
  } else {
    errors.parent.phone = "";
  }
};

const submit = () => {
  validateStudentName();
  validateStudentAge();
  validateParentPhone();

  if (formData.student.isValid && formData.parent.isValid) {
    alert("表单验证通过");
  } else {
    alert("请检查表单填写");
  }
};
</script>

<template>
  <form @submit.prevent="submit">
    <fieldset>
      <legend>学生基本信息</legend>

      <div class="form-group">
        <label for="student-name">学生姓名:</label>
        <input
          id="student-name"
          v-model="formData.student.name"
          @blur="validateStudentName"
          placeholder="请输入学生姓名"
        />
        <span class="error">{{ errors.student.name }}</span>
      </div>

      <div class="form-group">
        <label for="student-gender">学生性别:</label>
        <select id="student-gender" v-model="formData.student.gender">
          <option value="">请选择性别</option>
          <option value="male"></option>
          <option value="female"></option>
        </select>
        <span class="error">{{ errors.student.gender }}</span>
      </div>

      <div class="form-group">
        <label for="student-age">学生年龄:</label>
        <input
          id="student-age"
          type="number"
          v-model.number="formData.student.age"
          @blur="validateStudentAge"
          placeholder="6-25岁"
        />
        <span class="error">{{ errors.student.age }}</span>
      </div>
    </fieldset>

    <fieldset>
      <legend>家长联系信息</legend>

      <div class="form-group">
        <label for="parent-name">家长姓名:</label>
        <input
          id="parent-name"
          v-model="formData.parent.name"
          placeholder="请输入家长姓名"
        />
        <span class="error">{{ errors.parent.name }}</span>
      </div>

      <div class="form-group">
        <label for="parent-phone">联系电话:</label>
        <input
          id="parent-phone"
          v-model="formData.parent.phone"
          @blur="validateParentPhone"
          placeholder="11位手机号码"
        />
        <span class="error">{{ errors.parent.phone }}</span>
      </div>
    </fieldset>

    <button type="submit">提交表单</button>
  </form>
</template>

<style scoped>
form {
  max-width: 600px;
  margin: 0 auto;
  padding: 20px;
}
fieldset {
  border: 1px solid #ddd;
  border-radius: 4px;
  padding: 15px;
  margin-bottom: 20px;
}
legend {
  padding: 0 10px;
  font-weight: bold;
}
.form-group {
  margin-bottom: 15px;
}
label {
  display: inline-block;
  width: 100px;
  text-align: right;
  margin-right: 10px;
}
input,
select {
  padding: 8px;
  border: 1px solid #ddd;
  border-radius: 4px;
  width: 200px;
}
.error {
  color: red;
  font-size: 12px;
  margin-left: 110px;
  display: block;
}
button {
  padding: 10px 20px;
  background: #42b983;
  color: white;
  border: none;
  border-radius: 4px;
  cursor: pointer;
}
button:hover {
  background: #369f6b;
}
</style>

1747669149520.jpg

本文将基于提供的学生信息表单代码,详细讲解如何使用Vue3原生方式实现表单验证功能。这个实现方案不依赖任何第三方验证库,完全使用Vue3的响应式特性和原生JavaScript实现。

表单验证的核心设计

1. 数据结构设计

表单数据使用Vue3的reactive创建响应式对象,分为学生信息和家长信息两个部分:

const formData = reactive({
  student: {
    name: "",
    gender: "",
    age: null,
    isValid: computed(() => { /* 验证逻辑 */ })
  },
  parent: {
    name: "",
    phone: "",
    isValid: computed(() => { /* 验证逻辑 */ })
  }
})

每个字段都有对应的isValid计算属性,用于实时判断该部分数据是否有效。

2. 错误信息管理

单独定义errors对象来存储验证错误信息:

const errors = reactive({
  student: {
    name: "",
    gender: "",
    age: "",
  },
  parent: {
    name: "",
    phone: "",
  }
})

这种结构与表单数据保持一致的层级关系,便于管理和访问。

验证函数实现

1. 学生姓名验证

const validateStudentName = () => {
  if (!formData.student.name) {
    errors.student.name = "姓名不能为空";
  } else if (formData.student.name.length < 2) {
    errors.student.name = "姓名至少2个字符";
  } else {
    errors.student.name = "";
  }
};

验证逻辑:

  • 非空检查
  • 最小长度检查
  • 验证通过时清空错误信息

2. 学生年龄验证

const validateStudentAge = () => {
  if (!formData.student.age) {
    errors.student.age = "年龄不能为空";
  } else if (formData.student.age < 6) {
    errors.student.age = "年龄不能小于6岁";
  } else if (formData.student.age > 25) {
    errors.student.age = "年龄不能大于25岁";
  } else {
    errors.student.age = "";
  }
};

验证逻辑:

  • 非空检查
  • 最小值检查
  • 最大值检查
  • 验证通过时清空错误信息

3. 家长手机号验证

const validateParentPhone = () => {
  if (!formData.parent.phone) {
    errors.parent.phone = "手机号不能为空";
  } else if (!/^1[3-9]\d{9}$/.test(formData.parent.phone)) {
    errors.parent.phone = "请输入有效的手机号";
  } else {
    errors.parent.phone = "";
  }
};

验证逻辑:

  • 非空检查
  • 正则表达式验证手机号格式
  • 验证通过时清空错误信息

表单提交处理

const submit = () => {
  validateStudentName();
  validateStudentAge();
  validateParentPhone();

  if (formData.student.isValid && formData.parent.isValid) {
    alert("表单验证通过");
  } else {
    alert("请检查表单填写");
  }
};

提交时执行所有验证函数,并检查各部分数据的isValid状态。

模板结构

模板使用标准的HTML表单元素,结合Vue指令:

<form @submit.prevent="submit">
  <fieldset>
    <legend>学生基本信息</legend>
    <!-- 表单字段 -->
  </fieldset>
  
  <fieldset>
    <legend>家长联系信息</legend>
    <!-- 表单字段 -->
  </fieldset>
  
  <button type="submit">提交表单</button>
</form>

每个表单字段都绑定到对应的数据属性和验证函数:

<input
  id="student-name"
  v-model="formData.student.name"
  @blur="validateStudentName"
  placeholder="请输入学生姓名"
/>
<span class="error">{{ errors.student.name }}</span>

样式设计

样式部分使用scoped CSS,确保只影响当前组件:

.form-group {
  margin-bottom: 15px;
}
.error {
  color: red;
  font-size: 12px;
  margin-left: 110px;
  display: block;
}
/* 其他样式... */

实现优势

  1. 响应式验证‌:利用Vue3的响应式系统,数据变化自动触发界面更新
  2. 即时反馈‌:通过@blur事件在用户离开输入框时立即验证
  3. 结构化设计‌:数据和错误信息采用相同结构,便于维护
  4. 计算属性‌:使用computed实现自动验证状态计算
  5. 原生实现‌:不依赖第三方库,减少项目体积和依赖