news 2026/6/13 3:45:53

Vue表单处理与验证全攻略:从v-model基础到自定义校验,案例多到直接抄

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Vue表单处理与验证全攻略:从v-model基础到自定义校验,案例多到直接抄

一、回忆一下 v-model 的本事

前面讲组件通信时我们拆过v-model,它是一个语法糖,本质是:value+@input的组合。在表单元素上,它能让数据和输入框双向绑定

1.1 普通文本框

vue

<template> <div> <!-- v-model 绑定到 username 这个 ref 上 用户在输入框里打字,username 自动更新; 修改 username 的值,输入框也会自动变 --> <input v-model="username" placeholder="输入用户名" /> <p>你输入的是:{{ username }}</p> </div> </template> <script setup> import { ref } from 'vue' // 定义一个响应式变量存用户名 const username = ref('') </script>

为什么方便?
如果不写v-model,你得手动写:value="username"@input="e => username = e.target.value",麻烦又容易忘。

1.2 各种输入类型的绑定

vue

<template> <div> <!-- 文本框 --> <input v-model="text" placeholder="文本" /> <!-- 多行文本 --> <textarea v-model="textarea" placeholder="多行文本"></textarea> <!-- 复选框(单个布尔值) --> <input type="checkbox" v-model="checked" /> 同意协议 <p>是否同意:{{ checked }}</p> <!-- 多个复选框(绑定到数组) --> <input type="checkbox" v-model="hobbies" value="读书" /> 读书 <input type="checkbox" v-model="hobbies" value="跑步" /> 跑步 <input type="checkbox" v-model="hobbies" value="游泳" /> 游泳 <p>爱好:{{ hobbies }}</p> <!-- 单选框 --> <input type="radio" v-model="gender" value="男" /> 男 <input type="radio" v-model="gender" value="女" /> 女 <p>性别:{{ gender }}</p> <!-- 下拉选择框 --> <select v-model="city"> <option value="">请选择城市</option> <option value="北京">北京</option> <option value="上海">上海</option> <option value="广州">广州</option> </select> <p>城市:{{ city }}</p> </div> </template> <script setup> import { ref } from 'vue' const text = ref('') const textarea = ref('') const checked = ref(false) const hobbies = ref([]) // 多个复选框,绑数组 const gender = ref('') const city = ref('') </script>

记住:

  • 单个复选框 → 绑定布尔值。

  • 多个复选框 → 绑定到数组,value表示选中时的值。

  • 单选按钮 → 绑定到单个值。

  • 下拉框 → 绑定到选项的value


二、修饰符:帮你在绑定过程中做点手脚

v-model有几个实用修饰符,用起来非常省事。

2.1.lazy:懒同步

默认情况下v-model在每次input事件后更新数据。.lazy会改成在change事件后更新(即输入框失去焦点时才同步)。

vue

<input v-model.lazy="msg" placeholder="失去焦点才更新" /> <p>{{ msg }}</p>

什么时候用?不想每敲一个字就触发请求或校验,等用户输入完整再处理。

2.2.number:自动转数字

输入框里的值默认是字符串。如果你需要数字,可以用.number自动转换。

vue

<input v-model.number="age" type="number" /> <p>年龄 + 1:{{ age + 1 }}</p>

不加.numberage是字符串"25""25" + 1会变成"251"。加了.numberage就是数字25

2.3.trim:去除首尾空格

vue

<input v-model.trim="username" />

用户不小心在前面或后面打了空格,提交时自动去掉,非常贴心。


三、封装自定义表单组件

原生的<input>直接用没问题,但如果你要封装一个带标签、带校验样式、带错误提示的输入框,就需要自己写组件,并且让父组件能用v-model绑定。

3.1 自定义输入框组件

vue

<!-- MyInput.vue --> <template> <div class="my-input"> <!-- 标签文字 --> <label v-if="label">{{ label }}</label> <!-- 核心:用 :value + @input 实现 v-model --> <input :value="modelValue" @input="$emit('update:modelValue', $event.target.value)" :type="type" :placeholder="placeholder" :class="{ error: hasError }" /> <!-- 错误提示 --> <span v-if="hasError" class="error-msg">{{ errorMsg }}</span> </div> </template> <script setup> // 接收 v-model 绑定值 defineProps({ modelValue: { type: String, default: '' }, // 其他配置项 label: String, // 标签文字 type: { type: String, default: 'text' }, placeholder: String, hasError: Boolean, // 是否显示错误状态 errorMsg: String // 错误提示文字 }) // 声明事件 defineEmits(['update:modelValue']) </script> <style scoped> .my-input { margin-bottom: 10px; } .my-input label { display: block; margin-bottom: 5px; font-weight: bold; } .my-input input { padding: 6px 12px; border: 1px solid #ccc; border-radius: 4px; width: 100%; box-sizing: border-box; } .my-input input.error { border-color: red; } .error-msg { color: red; font-size: 12px; } </style>

父组件使用:

vue

<template> <div> <MyInput v-model="form.username" label="用户名" placeholder="请输入用户名" :has-error="errors.username" error-msg="用户名不能为空" /> <p>绑定值:{{ form.username }}</p> </div> </template> <script setup> import { reactive, ref } from 'vue' import MyInput from './MyInput.vue' const form = reactive({ username: '' }) const errors = reactive({ username: false }) // 可以结合 watch 做校验 </script>

四、表单验证:最简单的校验方式

实际项目里,表单提交前必须校验。我们先从最基础的手动校验开始。

4.1 手动校验

vue

<template> <div> <form @submit.prevent="handleSubmit"> <!-- 用户名 --> <div> <label>用户名</label> <input v-model.trim="form.username" /> <span v-if="errors.username" style="color:red;">{{ errors.username }}</span> </div> <!-- 密码 --> <div> <label>密码</label> <input v-model="form.password" type="password" /> <span v-if="errors.password" style="color:red;">{{ errors.password }}</span> </div> <button type="submit">提交</button> </form> </div> </template> <script setup> import { reactive, ref } from 'vue' const form = reactive({ username: '', password: '' }) // 存放错误信息 const errors = reactive({ username: '', password: '' }) // 校验函数 function validate() { let isValid = true // 重置错误 errors.username = '' errors.password = '' // 校验用户名 if (!form.username) { errors.username = '用户名不能为空' isValid = false } else if (form.username.length < 3) { errors.username = '用户名至少3个字符' isValid = false } // 校验密码 if (!form.password) { errors.password = '密码不能为空' isValid = false } else if (form.password.length < 6) { errors.password = '密码至少6位' isValid = false } return isValid } function handleSubmit() { if (validate()) { alert('提交成功!' + JSON.stringify(form)) // 这里发请求 } } </script>

流程很简单:定义校验函数,逐个检查字段,有错就放进errors对象里,页面显示对应的错误信息。提交时调用校验,通过了才发请求。


五、实战案例:完整的注册表单

来做一个稍微复杂的注册页面,包含用户名、邮箱、密码、确认密码、手机号。每个字段都有实时校验和提交时校验。

vue

<template> <div class="register"> <h2>用户注册</h2> <form @submit.prevent="handleSubmit"> <!-- 用户名 --> <div class="form-item"> <label>用户名</label> <input v-model.trim="form.username" @blur="validateField('username')" :class="{ error: errors.username }" placeholder="3-10位字符" /> <span class="error-msg" v-if="errors.username">{{ errors.username }}</span> </div> <!-- 邮箱 --> <div class="form-item"> <label>邮箱</label> <input v-model.trim="form.email" @blur="validateField('email')" :class="{ error: errors.email }" placeholder="example@mail.com" /> <span class="error-msg" v-if="errors.email">{{ errors.email }}</span> </div> <!-- 密码 --> <div class="form-item"> <label>密码</label> <input v-model="form.password" type="password" @blur="validateField('password')" :class="{ error: errors.password }" placeholder="至少6位" /> <span class="error-msg" v-if="errors.password">{{ errors.password }}</span> </div> <!-- 确认密码 --> <div class="form-item"> <label>确认密码</label> <input v-model="form.rePassword" type="password" @blur="validateField('rePassword')" :class="{ error: errors.rePassword }" placeholder="再次输入密码" /> <span class="error-msg" v-if="errors.rePassword">{{ errors.rePassword }}</span> </div> <!-- 手机号 --> <div class="form-item"> <label>手机号</label> <input v-model="form.phone" @blur="validateField('phone')" :class="{ error: errors.phone }" placeholder="11位手机号" /> <span class="error-msg" v-if="errors.phone">{{ errors.phone }}</span> </div> <!-- 同意协议 --> <div class="form-item"> <label> <input type="checkbox" v-model="form.agree" /> 我已阅读并同意《用户协议》 </label> <span class="error-msg" v-if="errors.agree">{{ errors.agree }}</span> </div> <button type="submit">注册</button> </form> </div> </template> <script setup> import { reactive } from 'vue' // 表单数据 const form = reactive({ username: '', email: '', password: '', rePassword: '', phone: '', agree: false }) // 错误对象 const errors = reactive({ username: '', email: '', password: '', rePassword: '', phone: '', agree: '' }) // 单个字段校验规则 function validateField(field) { switch (field) { case 'username': if (!form.username) { errors.username = '用户名不能为空' } else if (form.username.length < 3 || form.username.length > 10) { errors.username = '用户名需3-10位字符' } else { errors.username = '' } break case 'email': if (!form.email) { errors.email = '邮箱不能为空' } else if (!/^\S+@\S+\.\S+$/.test(form.email)) { errors.email = '邮箱格式不正确' } else { errors.email = '' } break case 'password': if (!form.password) { errors.password = '密码不能为空' } else if (form.password.length < 6) { errors.password = '密码至少6位' } else { errors.password = '' } // 如果确认密码已填,顺带校验一下是否一致 if (form.rePassword && form.password !== form.rePassword) { errors.rePassword = '两次密码不一致' } else if (form.rePassword) { errors.rePassword = '' } break case 'rePassword': if (!form.rePassword) { errors.rePassword = '请确认密码' } else if (form.rePassword !== form.password) { errors.rePassword = '两次密码不一致' } else { errors.rePassword = '' } break case 'phone': if (!form.phone) { errors.phone = '手机号不能为空' } else if (!/^1[3-9]\d{9}$/.test(form.phone)) { errors.phone = '手机号格式不正确' } else { errors.phone = '' } break } } // 校验全部字段 function validateAll() { validateField('username') validateField('email') validateField('password') validateField('rePassword') validateField('phone') // 协议单独校验 if (!form.agree) { errors.agree = '请同意用户协议' } else { errors.agree = '' } // 检查是否有错误 return Object.values(errors).every(msg => msg === '') } // 提交 function handleSubmit() { if (validateAll()) { alert('注册成功!') // 这里发送请求给后端 } } </script> <style scoped> .register { max-width: 400px; margin: 0 auto; } .form-item { margin-bottom: 15px; } .form-item label { display: block; margin-bottom: 5px; } .form-item input[type="text"], .form-item input[type="password"] { width: 100%; padding: 6px 12px; border: 1px solid #ccc; border-radius: 4px; box-sizing: border-box; } .form-item input.error { border-color: red; } .error-msg { color: red; font-size: 12px; } button { width: 100%; padding: 8px; background: #409eff; color: white; border: none; border-radius: 4px; cursor: pointer; } </style>

设计思路:

  • validateField负责单个字段校验,失去焦点时触发(@blur),实时给用户反馈。

  • validateAll提交时校验所有字段,包括必须勾选的协议。

  • 错误信息统一存在errors对象里,页面根据它来显示。


六、总结

今天我们学了:

  • v-model的基本绑定和各种输入类型。

  • 修饰符.lazy.number.trim的用法。

  • 封装支持v-model的自定义表单组件。

  • 手动校验的完整流程:字段校验、错误对象、实时反馈、提交检查。

这些知识足够你应对大部分项目中的表单需求。如果你想把校验逻辑抽出来复用,还可以结合之前学的组合式函数,封装一个useFormValidation,这个我们以后可以单独聊。

有问题评论区说,看到就回。下篇见!

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/6/13 3:44:54

数据入队模块的-ExeModule

DataIn.ExeModule() 方法解析 这个方法与刚才的 DataOut 是配对的——DataIn 是生产者&#xff08;往队列写数据&#xff09;&#xff0c;DataOut 是消费者&#xff08;从队列读数据&#xff09;。对照着看会更容易理解。1. 确保队列存在&#xff08;懒创建&#xff09; if (!S…

作者头像 李华
网站建设 2026/6/13 3:39:52

<p>抚顺的街头巷尾,贵金属回收店铺星罗棋布,从黄金、白银到铂金,各类回收需求日益旺盛。为了帮大家拨云见日,找到真正靠谱的服务商,小编不辞辛劳,精心梳理了一份关于抚顺本地诚信回收店铺的参考指南。以下便

福州作为福建省的省会城市&#xff0c;贵金属回收市场向来鱼龙混杂&#xff0c;不少市民在出手黄金、白银或铂金时&#xff0c;最怕遇到压价、扣秤甚至调包等糟心事。为了方便大家找到真正靠谱的回收渠道&#xff0c;小编特意实地探访并多方核实&#xff0c;整理出一份关于福州…

作者头像 李华
网站建设 2026/6/13 3:37:59

ACE2005数据集事件抽取实战:用Transformers库跑通第一个Demo

ACE2005数据集事件抽取实战&#xff1a;零基础快速搭建第一个模型刚接触NLP事件抽取时&#xff0c;面对复杂的论文和数据集总让人望而却步。今天我们就用最直接的方式&#xff0c;带你在30分钟内基于ACE2005数据集跑通第一个事件抽取模型。不需要理解繁琐的理论&#xff0c;只需…

作者头像 李华
网站建设 2026/6/13 3:35:54

2025年周口工商年报怎么选?避开这5个常见误区

当经营者搜索“求推荐周口工商年报公司”时&#xff0c;往往已经意识到年报公示并非简单填表&#xff0c;而是一项涉及合规风险、时间节点和财务数据准确性的正式申报行为。工商年报是市场主体通过国家企业信用信息公示系统向监管部门报送上一年度经营状况的法定义务&#xff0…

作者头像 李华