Vue中v-model的使用技巧和表单处理方案
一、回忆一下 v-model 的本事
前面讲组件通信时我们拆过 v-model,它是一个语法糖,本质是 :value + @input 的组合。在表单元素上,它能让数据和输入框双向绑定。
1.1 普通文本框
<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 各种输入类型的绑定
<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 事件后更新(即输入框失去焦点时才同步)。
<input v-model.lazy="msg" placeholder="失去焦点才更新" />
<p>{{ msg }}</p>什么时候用? 不想每敲一个字就触发请求或校验,等用户输入完整再处理。
2.2.number:自动转数字
输入框里的值默认是字符串。如果你需要数字,可以用 .number 自动转换。
<input v-model.number="age" type="number" />
<p>年龄 + 1:{{ age + 1 }}</p>不加 .number 时 age 是字符串 "25","25" + 1 会变成 "251"。加了 .number 后 age 就是数字 25。
2.3.trim:去除首尾空格
<input v-model.trim="username" />
用户不小心在前面或后面打了空格,提交时自动去掉,非常贴心。
三、封装自定义表单组件
原生的 <input> 直接用没问题,但如果你要封装一个带标签、带校验样式、带错误提示的输入框,就需要自己写组件,并且让父组件能用 v-model 绑定。
3.1 自定义输入框组件
<!-- 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>父组件使用:
<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 手动校验
<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 对象里,页面显示对应的错误信息。提交时调用校验,通过了才发请求。
五、实战案例:完整的注册表单
来做一个稍微复杂的注册页面,包含用户名、邮箱、密码、确认密码、手机号。每个字段都有实时校验和提交时校验。
<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,这个我们以后可以单独聊。
以上就是Vue中v-model的使用技巧和表单处理方案的详细内容,更多关于Vue v-model使用和表单处理的资料请关注脚本之家其它相关文章!
相关文章
laravel5.4+vue+element简单搭建的示例代码
本篇文章主要介绍了laravel5.4+vue+element简单搭建的示例代码,具有一定的参考价值,感兴趣的小伙伴们可以参考一下2017-08-08
Vue导出Excel,后端返回的文件流,前端接收后无法打开文件的解决
在使用Vue导出Excel时,如果后端返回文件流,前端接收后无法打开文件,通常是由于响应类型设置不正确导致的,通过设置axios请求接口时的响应类型为`blob`,并使用blob转换文件流,可以解决文件损坏无法打开的问题2025-12-12
解决ant Design中Select设置initialValue时的大坑
这篇文章主要介绍了解决ant Design中Select设置initialValue时的大坑,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧2020-10-10


最新评论