大家好,我是程序员柏风,AI 最全的 Vibecoding 学习地图 http://coderoadmap.cn 作者。
表单处理
这一课你将能做到
- 用
v-model实现输入框、文本域、复选框、单选框、下拉框的双向绑定 - 使用
.lazy、.number、.trim修饰符优化表单体验 - 在自定义组件上实现
v-model - 编写基础表单校验逻辑
为什么要学
表单是用户输入数据的「入口」。登录、注册、搜索、设置,全是表单。Vue 的 v-model 把「取值 → 更新数据」和「数据变 → 更新视图」自动化,是双向绑定的核心。
人话:v-model 就像「自动同步的笔记本」。你在左边写(用户输入),右边自动更新(数据变化);你在右边改(代码修改),左边自动刷新(视图更新)。
先建立图像
<script setup>
import { ref } from 'vue'
const username = ref('')
const agreed = ref(false)
</script>
<template>
<input v-model="username" placeholder="用户名">
<p>你输入了:{{ username }}</p>
<label>
<input type="checkbox" v-model="agreed"> 同意协议
</label>
<p>同意:{{ agreed }}</p>
</template>
跟着做
步骤 1:各种表单控件的 v-model
<script setup>
import { ref } from 'vue'
const text = ref('')
const textarea = ref('')
const checked = ref(false)
const checkedNames = ref([])
const picked = ref('')
const selected = ref('')
</script>
<template>
<!-- 文本框 -->
<input v-model="text" placeholder="单行文本">
<!-- 文本域 -->
<textarea v-model="textarea" placeholder="多行文本"></textarea>
<!-- 单个复选框 -->
<input type="checkbox" v-model="checked"> 勾选
<!-- 多个复选框(绑定到数组) -->
<input type="checkbox" value="游泳" v-model="checkedNames"> 游泳
<input type="checkbox" value="跑步" v-model="checkedNames"> 跑步
<!-- 单选框 -->
<input type="radio" value="男" v-model="picked"> 男
<input type="radio" value="女" v-model="picked"> 女
<!-- 下拉框 -->
<select v-model="selected">
<option disabled value="">请选择</option>
<option value="bj">北京</option>
<option value="sh">上海</option>
</select>
</template>
步骤 2:修饰符
<script setup>
import { ref } from 'vue'
const lazyText = ref('')
const age = ref('')
const trimmed = ref('')
</script>
<template>
<!-- .lazy:失去焦点或回车后才更新 -->
<input v-model.lazy="lazyText" placeholder="懒更新">
<!-- .number:自动转数字 -->
<input v-model.number="age" type="number" placeholder="数字">
<!-- .trim:自动去首尾空格 -->
<input v-model.trim="trimmed" placeholder="去空格">
</template>
步骤 3:自定义组件的 v-model
<!-- CustomInput.vue -->
<script setup>
const props = defineProps({ modelValue: String })
const emit = defineEmits(['update:modelValue'])
function onInput(e) {
emit('update:modelValue', e.target.value)
}
</script>
<template>
<input :value="modelValue" @input="onInput">
</template>
<!-- 父组件 -->
<script setup>
import { ref } from 'vue'
import CustomInput from './CustomInput.vue'
const val = ref('')
</script>
<template>
<CustomInput v-model="val" />
</template>
步骤 4:多个 v-model 绑定
<!-- UserForm.vue -->
<script setup>
const props = defineProps({
firstName: String,
lastName: String
})
const emit = defineEmits(['update:firstName', 'update:lastName'])
</script>
<template>
<input :value="firstName" @input="emit('update:firstName', $event.target.value)">
<input :value="lastName" @input="emit('update:lastName', $event.target.value)">
</template>
<UserForm v-model:first-name="first" v-model:last-name="last" />
步骤 5:基础表单校验
<script setup>
import { ref, computed } from 'vue'
const email = ref('')
const password = ref('')
const emailError = computed(() => {
if (!email.value) return '邮箱不能为空'
if (!/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email.value)) return '邮箱格式错误'
return ''
})
const passwordError = computed(() => {
if (!password.value) return '密码不能为空'
if (password.value.length < 6) return '密码至少 6 位'
return ''
})
const canSubmit = computed(() => !emailError.value && !passwordError.value)
function onSubmit() {
if (canSubmit.value) {
console.log('提交', { email: email.value })
}
}
</script>
<template>
<form @submit.prevent="onSubmit">
<div>
<input v-model="email" placeholder="邮箱">
<span v-if="emailError">{{ emailError }}</span>
</div>
<div>
<input v-model="password" type="password" placeholder="密码">
<span v-if="passwordError">{{ passwordError }}</span>
</div>
<button :disabled="!canSubmit">提交</button>
</form>
</template>
讲清楚要点
- v-model 是语法糖:
:value="x" @input="x = $event.target.value"的简写。 - 不同控件绑定方式不同:checkbox 绑布尔值或数组,radio 绑选中值,select 绑选中项。
.lazy减少更新频率:适合输入框搜索建议,避免频繁触发请求。.number自动类型转换:type="number"的输入框默认返回字符串,.number转成数字。- 自定义 v-model 用
modelValue+update:modelValue:Vue 3 默认 prop 名和事件名。
你可能会懵的地方
-
v-model 和 :value + @input 有什么区别? 功能一样,v-model 是语法糖。但 v-model 在组件上还能绑定多个值(如
v-model:title)。 -
checkbox 绑定数组时,为什么 value 必须写? 不写 value 时,checkbox 绑定的是布尔值;写 value 后,勾选时把 value 加入数组,取消时移除。
-
怎么在 select 上设置默认值? 给绑定的 ref 一个初始值,如
const selected = ref('bj')。 -
v-model 能绑定 computed 吗? 能,但 computed 必须可写(有 setter),否则只能读。
-
表单校验应该什么时候触发? 输入时(实时反馈)、失焦时(减少打扰)、提交时(最终确认),根据场景组合。
错法对照
| 错法 | 问题 | 正解 |
|---|---|---|
在 input 上同时写 :value 和 v-model | 冲突,v-model 已经包含 :value | 只写 v-model |
自定义组件用 value + input 事件 | Vue 3 默认是 modelValue + update:modelValue | 遵循约定或显式配置 |
| checkbox 绑定数组但不写 value | 勾选后数组里是空字符串 | 每个 checkbox 写唯一的 value |
| 用 v-model.number 但输入非数字 | 得到 NaN | 配合 type="number" 或校验 |
| 提交按钮不做 disabled 控制 | 用户可提交非法数据 | 用 computed 控制按钮状态 |
卡点急救
- v-model 绑定对象属性不更新:确保对象是 reactive 或 ref 的嵌套属性,如
user.name。 - 自定义组件 v-model 不生效:检查 emit 的事件名是否是
update:modelValue,大小写敏感。 - select 默认值不显示:确保绑定值和 option 的 value 类型一致(字符串 vs 数字)。
面试常问
-
v-model 的原理? 语法糖,编译为
:value+@input。组件上编译为:modelValue+@update:modelValue。 -
Vue 2 和 Vue 3 的 v-model 区别? Vue 3 默认 prop 名从
value改为modelValue,支持多个 v-model,支持自定义修饰符。 -
怎么实现自定义修饰符? 组件接收
modelModifiersprop,如v-model.capitalize,在 emit 前处理值。
自测三问
v-model在原生 input 和自定义组件上的实现分别是什么?.lazy、.number、.trim三个修饰符分别解决什么问题?- 如何在一个组件上实现多个 v-model 绑定?
小结一句话
v-model 是 Vue 表单的核心,一行代码实现双向绑定;掌握各种控件用法、修饰符和自定义组件的 v-model,就能处理所有表单场景。
延伸
- Vue 官方文档 - 表单输入绑定
- 下一课:Vue Router——路由配置、动态路由、导航守卫。