v-model指令实现表单元素与Vue数据的双向绑定
v-model指令在表单输入元素上创建双向数据绑定。它会根据控件类型自动选取正确的方法来更新元素。
<!-- 基础语法 -->
<input v-model="message" placeholder="编辑我">
<p>消息是: {{ message }}</p>
<!-- 等价于 -->
<input :value="message" @input="message = $event.target.value">
使用v-model绑定到<input>元素:
<div id="text-example">
<input v-model="message" placeholder="请输入内容">
<p>输入的内容是: {{ message }}</p>
<!-- 不同类型的输入框 -->
<div class="mb-2">
<label>用户名:</label>
<input v-model="username" type="text" class="form-control">
</div>
<div class="mb-2">
<label>邮箱:</label>
<input v-model="email" type="email" class="form-control">
</div>
<div class="mb-2">
<label>密码:</label>
<input v-model="password" type="password" class="form-control">
</div>
<div class="mb-2">
<label>网址:</label>
<input v-model="url" type="url" class="form-control">
</div>
<div class="mb-2">
<label>数字:</label>
<input v-model="number" type="number" class="form-control">
</div>
<div class="mb-2">
<label>范围: {{ rangeValue }}</label>
<input v-model="rangeValue" type="range" min="0" max="100" class="form-range">
</div>
</div>
<script>
new Vue({
el: '#text-example',
data: {
message: '',
username: '',
email: '',
password: '',
url: '',
number: 0,
rangeValue: 50
}
});
</script>
绑定到<textarea>元素:
<div id="textarea-example">
<span>多行消息是:</span>
<p style="white-space: pre-line;">{{ message }}</p>
<br>
<textarea v-model="message" placeholder="添加多行文本"></textarea>
<!-- 注意:在<textarea>中使用插值无效 -->
<!-- 错误示例 -->
<textarea>{{ message }}</textarea> <!-- 不会工作! -->
<!-- 正确示例 -->
<textarea v-model="message"></textarea>
</div>
<script>
new Vue({
el: '#textarea-example',
data: {
message: '这是初始文本\n第二行文本'
}
});
</script>
<textarea>{{ text }}</textarea>不会工作!
应该使用v-model来绑定数据。
单个复选框绑定到布尔值,多个复选框绑定到数组:
<div id="checkbox-example">
<!-- 单个复选框 -->
<div class="form-check">
<input type="checkbox" id="agree" v-model="checked" class="form-check-input">
<label for="agree" class="form-check-label">我同意条款</label>
</div>
<p>选择状态: {{ checked }}</p>
<!-- 多个复选框绑定到数组 -->
<div class="mt-3">
<p>选择你喜欢的编程语言:</p>
<div class="form-check">
<input type="checkbox" id="javascript" value="JavaScript" v-model="languages" class="form-check-input">
<label for="javascript" class="form-check-label">JavaScript</label>
</div>
<div class="form-check">
<input type="checkbox" id="python" value="Python" v-model="languages" class="form-check-input">
<label for="python" class="form-check-label">Python</label>
</div>
<div class="form-check">
<input type="checkbox" id="java" value="Java" v-model="languages" class="form-check-input">
<label for="java" class="form-check-label">Java</label>
</div>
<div class="form-check">
<input type="checkbox" id="php" value="PHP" v-model="languages" class="form-check-input">
<label for="php" class="form-check-label">PHP</label>
</div>
<p>已选择: {{ languages }}</p>
</div>
<!-- 自定义值的复选框 -->
<div class="mt-3">
<input type="checkbox" v-model="toggle" true-value="是" false-value="否">
<label>开关状态: {{ toggle }}</label>
</div>
</div>
<script>
new Vue({
el: '#checkbox-example',
data: {
checked: false,
languages: [],
toggle: '否'
}
});
</script>
多个单选按钮绑定到同一个数据属性:
<div id="radio-example">
<p>选择你喜欢的框架:</p>
<div class="form-check">
<input type="radio" id="vue" value="Vue.js" v-model="framework" class="form-check-input">
<label for="vue" class="form-check-label">Vue.js</label>
</div>
<div class="form-check">
<input type="radio" id="react" value="React" v-model="framework" class="form-check-input">
<label for="react" class="form-check-label">React</label>
</div>
<div class="form-check">
<input type="radio" id="angular" value="Angular" v-model="framework" class="form-check-input">
<label for="angular" class="form-check-label">Angular</label>
</div>
<div class="form-check">
<input type="radio" id="svelte" value="Svelte" v-model="framework" class="form-check-input">
<label for="svelte" class="form-check-label">Svelte</label>
</div>
<p>你选择的框架是: {{ framework }}</p>
</div>
<script>
new Vue({
el: '#radio-example',
data: {
framework: 'Vue.js' // 默认选择
}
});
</script>
单选选择框绑定到字符串,多选选择框绑定到数组:
<div id="select-example">
<!-- 单选选择框 -->
<div class="mb-3">
<label>选择一个城市:</label>
<select v-model="selectedCity" class="form-select">
<option disabled value="">请选择</option>
<option value="beijing">北京</option>
<option value="shanghai">上海</option>
<option value="guangzhou">广州</option>
<option value="shenzhen">深圳</option>
</select>
<p>选择的城市: {{ selectedCity }}</p>
</div>
<!-- 多选选择框 -->
<div class="mb-3">
<label>选择多个技能 (按住Ctrl/Cmd选择):</label>
<select v-model="selectedSkills" multiple class="form-select">
<option value="html">HTML</option>
<option value="css">CSS</option>
<option value="javascript">JavaScript</option>
<option value="vue">Vue.js</option>
<option value="react">React</option>
<option value="node">Node.js</option>
</select>
<p>选择的技能: {{ selectedSkills }}</option>
</div>
<!-- 使用v-for动态渲染选项 -->
<div class="mb-3">
<label>选择一个框架:</label>
<select v-model="selectedFramework" class="form-select">
<option v-for="framework in frameworks" :value="framework.value">
{{ framework.name }}
</option>
</select>
<p>选择的框架: {{ selectedFramework }}</p>
</div>
</div>
<script>
new Vue({
el: '#select-example',
data: {
selectedCity: '',
selectedSkills: [],
selectedFramework: '',
frameworks: [
{ value: 'vue', name: 'Vue.js' },
{ value: 'react', name: 'React' },
{ value: 'angular', name: 'Angular' },
{ value: 'svelte', name: 'Svelte' }
]
}
});
</script>
v-model表达式的初始值未能匹配任何选项,<select>元素将被渲染为"未选中"状态。
在iOS中,这会使用户无法选择第一个选项。建议提供一个值为空的禁用选项。
对于单选按钮、复选框及选择框的选项,v-model绑定的值通常是静态字符串,但也可以绑定动态值:
<!-- 单选按钮 -->
<input type="radio" v-model="pick" value="a">
<!-- 复选框 -->
<input type="checkbox" v-model="toggle">
<!-- 选择框选项 -->
<select v-model="selected">
<option value="abc">ABC</option>
</select>
<!-- 单选按钮绑定动态值 -->
<input type="radio" v-model="pick" :value="dynamicValue">
<!-- 复选框绑定动态值 -->
<input type="checkbox" v-model="toggle" :true-value="dynamicTrueValue" :false-value="dynamicFalseValue">
<!-- 选择框选项绑定动态值 -->
<select v-model="selected">
<option :value="dynamicValue">
{{ dynamicText }}
</option>
</select>
v-model指令提供了三个修饰符来自动处理用户输入:
| 修饰符 | 描述 | 示例 | 应用场景 |
|---|---|---|---|
.lazy |
转为在change事件后同步数据 |
v-model.lazy="msg" |
避免频繁更新,提升性能 |
.number |
自动将用户的输入值转为数值类型 | v-model.number="age" |
处理数字输入,避免字符串类型 |
.trim |
自动过滤用户输入的首尾空白字符 | v-model.trim="name" |
用户名、邮箱等需要去除空格的场景 |
<div id="modifier-example">
<!-- .lazy 修饰符 -->
<div class="mb-3">
<label>实时更新:</label>
<input v-model="message1" placeholder="输入时实时更新" class="form-control">
<p>实时值: "{{ message1 }}"</p>
<label>延迟更新(.lazy):</label>
<input v-model.lazy="message2" placeholder="失焦后更新" class="form-control">
<p>延迟值: "{{ message2 }}"</p>
</div>
<!-- .number 修饰符 -->
<div class="mb-3">
<label>年龄(字符串):</label>
<input v-model="age1" type="number" class="form-control">
<p>类型: {{ typeof age1 }}, 值: {{ age1 }}</p>
<label>年龄(数字):</label>
<input v-model.number="age2" type="number" class="form-control">
<p>类型: {{ typeof age2 }}, 值: {{ age2 }}</p>
</div>
<!-- .trim 修饰符 -->
<div class="mb-3">
<label>用户名(未修剪):</label>
<input v-model="username1" placeholder="输入带空格的用户名" class="form-control">
<p>原始长度: {{ username1.length }}</p>
<label>用户名(修剪后):</label>
<input v-model.trim="username2" placeholder="输入带空格的用户名" class="form-control">
<p>修剪后长度: {{ username2.length }}</p>
</div>
</div>
<script>
new Vue({
el: '#modifier-example',
data: {
message1: '',
message2: '',
age1: '',
age2: '',
username1: '',
username2: ''
}
});
</script>
综合使用各种表单元素和验证:
<div id="complete-form-example">
<form @submit.prevent="submitForm">
<!-- 文本输入 -->
<div class="mb-3">
<label for="name" class="form-label">姓名</label>
<input type="text" id="name" v-model.trim="form.name"
class="form-control" :class="{ 'is-invalid': errors.name }"
placeholder="请输入姓名" required>
<div class="invalid-feedback" v-if="errors.name">
{{ errors.name }}
</div>
</div>
<!-- 邮箱输入 -->
<div class="mb-3">
<label for="email" class="form-label">邮箱地址</label>
<input type="email" id="email" v-model.trim="form.email"
class="form-control" :class="{ 'is-invalid': errors.email }"
placeholder="name@example.com" required>
<div class="invalid-feedback" v-if="errors.email">
{{ errors.email }}
</div>
</div>
<!-- 密码输入 -->
<div class="mb-3">
<label for="password" class="form-label">密码</label>
<input type="password" id="password" v-model="form.password"
class="form-control" :class="{ 'is-invalid': errors.password }"
placeholder="至少6个字符" required>
<div class="invalid-feedback" v-if="errors.password">
{{ errors.password }}
</div>
</div>
<!-- 年龄输入 -->
<div class="mb-3">
<label for="age" class="form-label">年龄</label>
<input type="number" id="age" v-model.number="form.age"
class="form-control" :class="{ 'is-invalid': errors.age }"
min="0" max="150">
<div class="invalid-feedback" v-if="errors.age">
{{ errors.age }}
</div>
</div>
<!-- 性别单选按钮 -->
<div class="mb-3">
<label class="form-label">性别</label>
<div class="form-check">
<input type="radio" id="male" value="male" v-model="form.gender" class="form-check-input">
<label for="male" class="form-check-label">男</label>
</div>
<div class="form-check">
<input type="radio" id="female" value="female" v-model="form.gender" class="form-check-input">
<label for="female" class="form-check-label">女</label>
</div>
<div class="form-check">
<input type="radio" id="other" value="other" v-model="form.gender" class="form-check-input">
<label for="other" class="form-check-label">其他</label>
</div>
</div>
<!-- 兴趣复选框 -->
<div class="mb-3">
<label class="form-label">兴趣爱好</label>
<div class="form-check">
<input type="checkbox" id="reading" value="reading" v-model="form.hobbies" class="form-check-input">
<label for="reading" class="form-check-label">阅读</label>
</div>
<div class="form-check">
<input type="checkbox" id="sports" value="sports" v-model="form.hobbies" class="form-check-input">
<label for="sports" class="form-check-label">运动</label>
</div>
<div class="form-check">
<input type="checkbox" id="music" value="music" v-model="form.hobbies" class="form-check-input">
<label for="music" class="form-check-label">音乐</label>
</div>
<div class="form-check">
<input type="checkbox" id="travel" value="travel" v-model="form.hobbies" class="form-check-input">
<label for="travel" class="form-check-label">旅行</label>
</div>
</div>
<!-- 城市选择 -->
<div class="mb-3">
<label for="city" class="form-label">所在城市</label>
<select id="city" v-model="form.city" class="form-select">
<option value="">请选择城市</option>
<option value="beijing">北京</option>
<option value="shanghai">上海</option>
<option value="guangzhou">广州</option>
<option value="shenzhen">深圳</option>
<option value="hangzhou">杭州</option>
</select>
</div>
<!-- 自我介绍 -->
<div class="mb-3">
<label for="bio" class="form-label">自我介绍</label>
<textarea id="bio" v-model.lazy="form.bio" class="form-control"
rows="3" placeholder="简单介绍一下自己..."></textarea>
</div>
<!-- 协议同意 -->
<div class="mb-3 form-check">
<input type="checkbox" id="agree" v-model="form.agree" class="form-check-input">
<label for="agree" class="form-check-label">我同意用户协议</label>
</div>
<!-- 提交按钮 -->
<button type="submit" class="btn btn-primary" :disabled="isSubmitting">
{{ isSubmitting ? '提交中...' : '提交注册' }}
</button>
<button type="button" @click="resetForm" class="btn btn-secondary ms-2">
重置表单
</button>
</form>
<!-- 表单数据预览 -->
<div class="mt-4">
<h6>表单数据预览:</h6>
<pre>{{ JSON.stringify(form, null, 2) }}</pre>
</div>
</div>
<script>
new Vue({
el: '#complete-form-example',
data: {
isSubmitting: false,
form: {
name: '',
email: '',
password: '',
age: 18,
gender: 'male',
hobbies: [],
city: '',
bio: '',
agree: false
},
errors: {}
},
methods: {
validateForm() {
this.errors = {};
// 验证姓名
if (!this.form.name.trim()) {
this.errors.name = '姓名不能为空';
}
// 验证邮箱
if (!this.form.email) {
this.errors.email = '邮箱不能为空';
} else if (!this.isValidEmail(this.form.email)) {
this.errors.email = '请输入有效的邮箱地址';
}
// 验证密码
if (!this.form.password) {
this.errors.password = '密码不能为空';
} else if (this.form.password.length < 6) {
this.errors.password = '密码至少需要6个字符';
}
// 验证年龄
if (this.form.age < 0) {
this.errors.age = '年龄不能小于0';
} else if (this.form.age > 150) {
this.errors.age = '年龄不能大于150';
}
return Object.keys(this.errors).length === 0;
},
isValidEmail(email) {
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
return emailRegex.test(email);
},
submitForm() {
if (!this.validateForm()) {
return;
}
if (!this.form.agree) {
alert('请同意用户协议');
return;
}
this.isSubmitting = true;
// 模拟API调用
setTimeout(() => {
alert('注册成功!\n表单数据已提交。');
this.isSubmitting = false;
// 在实际应用中,这里会发送到服务器
console.log('表单提交:', this.form);
}, 1000);
},
resetForm() {
this.form = {
name: '',
email: '',
password: '',
age: 18,
gender: 'male',
hobbies: [],
city: '',
bio: '',
agree: false
};
this.errors = {};
}
}
});
</script>
.trim去除用户输入的首尾空格.number确保数字输入的正确类型.lazy减少频繁更新提升性能.lazy修饰符