什么是 v-model?
v-model
是 Vue 中最常用的指令之一,它实现了表单输入元素与 Vue 实例数据的双向绑定。这意味着:
- 当用户修改表单元素的值时,Vue 实例的数据会自动更新
- 当 Vue 实例数据变化时,表单元素的值也会自动更新
基本用法
在原生表单元素上使用
html
<template>
<div class="container">
<!-- 文本输入 -->
<div class="form-group">
<label>用户名:</label>
<input v-model="username" placeholder="输入用户名">
<p>当前值:{{ username }}</p>
</div>
<!-- 多行文本 -->
<div class="form-group">
<label>个人简介:</label>
<textarea v-model="bio" placeholder="输入个人简介"></textarea>
<p class="preview">预览:{{ bio }}</p>
</div>
<!-- 复选框 -->
<div class="form-group">
<label>
<input type="checkbox" v-model="agreed"> 我同意服务条款
</label>
<p v-if="agreed" class="success">已同意条款</p>
</div>
<!-- 单选按钮 -->
<div class="form-group">
<label>选择性别:</label>
<div class="radio-group">
<label>
<input type="radio" value="male" v-model="gender"> 男性
</label>
<label>
<input type="radio" value="female" v-model="gender"> 女性
</label>
<label>
<input type="radio" value="other" v-model="gender"> 其他
</label>
</div>
<p>选择结果:{{ gender }}</p>
</div>
<!-- 下拉选择 -->
<div class="form-group">
<label>选择城市:</label>
<select v-model="city">
<option disabled value="">请选择</option>
<option value="beijing">北京</option>
<option value="shanghai">上海</option>
<option value="guangzhou">广州</option>
<option value="shenzhen">深圳</option>
</select>
<p>所选城市:{{ city }}</p>
</div>
</div>
</template>
<script>
export default {
data() {
return {
username: '',
bio: '',
agreed: false,
gender: '',
city: ''
}
}
}
</script>
<style>
.container {
max-width: 600px;
margin: 0 auto;
padding: 20px;
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
}
.form-group {
margin-bottom: 25px;
padding: 15px;
border-radius: 8px;
background-color: #f8f9fa;
box-shadow: 0 2px 5px rgba(0,0,0,0.1);
}
label {
display: block;
margin-bottom: 8px;
font-weight: 600;
color: #333;
}
input[type="text"],
textarea,
select {
width: 100%;
padding: 10px;
border: 1px solid #ddd;
border-radius: 4px;
font-size: 16px;
margin-bottom: 10px;
}
input[type="checkbox"],
input[type="radio"] {
margin-right: 8px;
}
.radio-group {
display: flex;
gap: 15px;
margin: 10px 0;
}
.preview {
white-space: pre-wrap;
background-color: #fff;
padding: 10px;
border-radius: 4px;
border-left: 3px solid #42b983;
}
.success {
color: #42b983;
font-weight: 600;
}
p {
margin: 8px 0;
color: #555;
}
</style>
v-model 修饰符
Vue 为 v-model 提供了几个有用的修饰符:
1. .lazy
将 input 事件转换为 change 事件(在输入完成时更新)
html
<!-- 输入完成后才更新数据 -->
<input v-model.lazy="message">
2. .number
自动将用户输入转为数值类型
html
<input v-model.number="age" type="number">
3. .trim
自动去除用户输入的首尾空白字符
html
<input v-model.trim="username">
在自定义组件上使用 v-model
v-model 也可用于自定义组件,实现组件与父级数据的双向绑定:
Vue 2 的实现方式
在 Vue 2 中,组件上的 v-model 默认使用 value
属性和 input
事件:
html
<!-- 父组件 -->
<CustomInput v-model="message" />
<!-- 等价于 -->
<CustomInput :value="message" @input="message = $event" />
子组件实现:
html
<template>
<input
:value="value"
@input="$emit('input', $event.target.value)"
>
</template>
<script>
export default {
props: ['value']
}
</script>
Vue 3 的实现方式
Vue 3 默认使用 modelValue
属性和 update:modelValue
事件:
html
<!-- 父组件 -->
<CustomInput v-model="message" />
<!-- 等价于 -->
<CustomInput
:modelValue="message"
@update:modelValue="message = $event"
/>
子组件实现:
html
<template>
<input
:value="modelValue"
@input="$emit('update:modelValue', $event.target.value)"
>
</template>
<script>
export default {
props: ['modelValue']
}
</script>
Vue 3 中的高级用法
多个 v-model 绑定
Vue 3 允许在单个组件上使用多个 v-model:
html
<UserForm
v-model:first-name="firstName"
v-model:last-name="lastName"
v-model:email="email"
/>
子组件实现:
html
<template>
<input :value="firstName" @input="$emit('update:firstName', $event.target.value)">
<input :value="lastName" @input="$emit('update:lastName', $event.target.value)">
<input :value="email" @input="$emit('update:email', $event.target.value)">
</template>
<script>
export default {
props: ['firstName', 'lastName', 'email'],
emits: ['update:firstName', 'update:lastName', 'update:email']
}
</script>
自定义修饰符
可以为自定义组件创建特定的修饰符:
html
<CustomInput v-model.capitalize="message" />
子组件实现:
html
<template>
<input
:value="modelValue"
@input="emitValue($event.target.value)"
>
</template>
<script>
export default {
props: {
modelValue: String,
modelModifiers: {
default: () => ({})
}
},
methods: {
emitValue(value) {
if (this.modelModifiers.capitalize) {
value = value.charAt(0).toUpperCase() + value.slice(1)
}
this.$emit('update:modelValue', value)
}
}
}
</script>
底层实现原理
v-model 本质上是语法糖,它结合了属性绑定和事件监听:
html
<input v-model="searchText">
<!-- 等价于 -->
<input
:value="searchText"
@input="searchText = $event.target.value"
>
对于组件:
html
<custom-input v-model="searchText"></custom-input>
<!-- 等价于 -->
<custom-input
:model-value="searchText"
@update:model-value="searchText = $event"
></custom-input>
最佳实践
- 表单验证:结合 v-model 和表单验证库(如 VeeValidate)
- 性能优化:对于复杂表单,考虑使用 .lazy 修饰符减少更新频率
- 组件设计:为自定义表单组件实现 v-model 接口
- 状态管理:在大型应用中,将表单状态存储在 Vuex 或 Pinia 中
- 无障碍访问:确保表单元素有正确的 label 和 aria 属性
总结
v-model 是 Vue 中处理表单数据的核心指令,它提供了简洁的双向绑定语法。通过理解其工作原理和各种修饰符,你可以更高效地处理表单交互。在自定义组件中使用 v-model 可以创建高度可复用的表单组件,提升开发效率。