Appearance
表单输入绑定
v-model 基础用法
Vue.js 提供了 v-model 指令,用于在表单元素上创建双向数据绑定。它会根据控件类型自动选择正确的方法来更新元素。
文本输入
vue
<template>
<div>
<p>Message is: {{ message }}</p>
<input v-model="message" placeholder="edit me">
</div>
</template>
<script>
export default {
data() {
return {
message: ''
}
}
}
</script>
多行文本
vue
<template>
<div>
<p>Multiline message is:</p>
<p style="white-space: pre-line;">{{ message }}</p>
<textarea v-model="message" placeholder="add multiple lines"></textarea>
</div>
</template>
<script>
export default {
data() {
return {
message: ''
}
}
}
</script>
复选框
单个复选框,绑定到布尔值:
vue
<template>
<div>
<input type="checkbox" id="checkbox" v-model="checked">
<label for="checkbox">{{ checked }}</label>
</div>
</template>
<script>
export default {
data() {
return {
checked: false
}
}
}
</script>
</template>
多个复选框,绑定到同一个数组:
vue
<template>
<div>
<input type="checkbox" id="jack" value="Jack" v-model="checkedNames">
<label for="jack">Jack</label>
<input type="checkbox" id="john" value="John" v-model="checkedNames">
<label for="john">John</label>
<input type="checkbox" id="mike" value="Mike" v-model="checkedNames">
<label for="mike">Mike</label>
<br>
<span>Checked names: {{ checkedNames }}</span>
</div>
</template>
<script>
export default {
data() {
return {
checkedNames: []
}
}
}
</script>
</template>
单选按钮
vue
<template>
<div>
<input type="radio" id="one" value="One" v-model="picked">
<label for="one">One</label>
<input type="radio" id="two" value="Two" v-model="picked">
<label for="two">Two</label>
<br>
<span>Picked: {{ picked }}</span>
</div>
</template>
<script>
export default {
data() {
return {
picked: ''
}
}
}
</script>
</template>
选择框
单选:
vue
<template>
<div>
<select v-model="selected">
<option disabled value="">Please select one</option>
<option>A</option>
<option>B</option>
<option>C</option>
</select>
<span>Selected: {{ selected }}</span>
</div>
</template>
<script>
export default {
data() {
return {
selected: ''
}
}
}
</script>
</template>
多选(绑定到一个数组):
vue
<template>
<div>
<select v-model="selected" multiple>
<option>A</option>
<option>B</option>
<option>C</option>
</select>
<br>
<span>Selected: {{ selected }}</span>
</div>
</template>
<script>
export default {
data() {
return {
selected: []
}
}
}
</script>
</template>
值绑定
对于单选按钮、复选框和选择框选项,v-model 绑定的值通常是静态字符串(对于复选框是布尔值):
vue
<!-- 当选中时,`picked` 为字符串 "a" -->
<input type="radio" v-model="picked" value="a">
但有时我们可能想将值绑定到 Vue 实例上的动态属性,这时可以使用 v-bind 来实现:
vue
<template>
<div>
<!-- 复选框 -->
<input
type="checkbox"
v-model="toggle"
:true-value="trueValue"
:false-value="falseValue"
>
<p>Toggle value: {{ toggle }}</p>
<!-- 单选按钮 -->
<input type="radio" v-model="pick" :value="aValue">
<input type="radio" v-model="pick" :value="bValue">
<p>Selected value: {{ pick }}</p>
</div>
</template>
<script>
export default {
data() {
return {
toggle: false,
trueValue: 'yes',
falseValue: 'no',
pick: null,
aValue: 'a',
bValue: 'b'
}
}
}
</script>
</template>
修饰符
v-model 指令提供了几个修饰符来处理常见的输入场景:
.lazy 修饰符
在默认情况下,v-model 在每次 input 事件后同步更新数据(除了 IME 组合期间)。你可以添加 lazy 修饰符,使 v-model 在 change 事件后同步:
vue
<template>
<div>
<p>Without lazy: {{ message1 }}</p>
<input v-model="message1">
<p>With lazy: {{ message2 }}</p>
<input v-model.lazy="message2">
</div>
</template>
<script>
export default {
data() {
return {
message1: '',
message2: ''
}
}
}
</script>
</template>
.number 修饰符
如果想自动将用户的输入值转为数字类型,可以给 v-model 添加 number 修饰符:
vue
<template>
<div>
<input v-model="age" type="number" placeholder="Input age">
<p>Age: {{ age }} (Type: {{ typeof age }})</p>
<input v-model.number="age2" type="number" placeholder="Input age with .number">
<p>Age2: {{ age2 }} (Type: {{ typeof age2 }})</p>
</div>
</template>
<script>
export default {
data() {
return {
age: '',
age2: ''
}
}
}
</script>
</template>
.trim 修饰符
如果要自动过滤用户输入的首尾空白字符,可以给 v-model 添加 trim 修饰符:
vue
<template>
<div>
<input v-model="msg1" placeholder="Without trim">
<p>Message without trim: "{{ msg1 }}"</p>
<input v-model.trim="msg2" placeholder="With trim">
<p>Message with trim: "{{ msg2 }}"</p>
</div>
</template>
<script>
export default {
data() {
return {
msg1: '',
msg2: ''
}
}
}
</script>
</template>
在组件上使用 v-model
自定义组件上的 v-model 默认情况下会利用名为 modelValue 的 prop 和名为 update:modelValue 的事件。
Vue 3 中的 v-model
vue
<!-- 子组件 ChildComponent.vue -->
<template>
<input
:value="modelValue"
@input="$emit('update:modelValue', $event.target.value)"
>
</template>
<script>
export default {
name: 'ChildComponent',
props: ['modelValue'],
emits: ['update:modelValue']
}
</script>
vue
<!-- 父组件使用 -->
<template>
<div>
<p>Parent message: {{ message }}</p>
<ChildComponent v-model="message" />
</div>
</template>
<script>
import ChildComponent from './ChildComponent.vue'
export default {
components: {
ChildComponent
},
data() {
return {
message: ''
}
}
}
</script>
</template>
使用 v-model 的参数
Vue 3 允许组件的 v-model 使用不同的 prop 和事件:
vue
<!-- ChildComponentWithArgs.vue -->
<template>
<div>
<input
:value="title"
@input="$emit('update:title', $event.target.value)"
placeholder="Title"
>
<input
:value="content"
@input="$emit('update:content', $event.target.value)"
placeholder="Content"
>
</div>
</template>
<script>
export default {
props: ['title', 'content'],
emits: ['update:title', 'update:content']
}
</script>
vue
<!-- 父组件 -->
<template>
<div>
<p>Title: {{ title }}</p>
<p>Content: {{ content }}</p>
<ChildComponentWithArgs
v-model:title="title"
v-model:content="content"
/>
</div>
</template>
<script>
import ChildComponentWithArgs from './ChildComponentWithArgs.vue'
export default {
components: {
ChildComponentWithArgs
},
data() {
return {
title: '',
content: ''
}
}
}
</script>
</template>
组合式API中的表单处理
在组合式API中,使用 ref 来管理表单数据:
vue
<script setup>
import { ref, computed } from 'vue'
const message = ref('')
const checked = ref(false)
const checkedNames = ref([])
const picked = ref('')
const selected = ref('')
const multiSelect = ref([])
// 使用计算属性进行表单验证
const isValid = computed(() => {
return message.value.trim().length > 0 && checked.value
})
function submitForm() {
if (isValid.value) {
console.log({
message: message.value,
checked: checked.value,
checkedNames: checkedNames.value,
picked: picked.value,
selected: selected.value,
multiSelect: multiSelect.value
})
}
}
</script>
<template>
<form @submit.prevent="submitForm">
<div>
<input v-model="message" placeholder="Message">
<span v-if="!message.trim()">Required</span>
</div>
<div>
<input type="checkbox" v-model="checked" id="agree">
<label for="agree">Agree to terms</label>
</div>
<div>
<input type="checkbox" value="Vue" v-model="checkedNames" id="vue">
<label for="vue">Vue</label>
<input type="checkbox" value="React" v-model="checkedNames" id="react">
<label for="react">React</label>
</div>
<div>
<input type="radio" value="option1" v-model="picked" id="option1">
<label for="option1">Option 1</label>
<input type="radio" value="option2" v-model="picked" id="option2">
<label for="option2">Option 2</label>
</div>
<div>
<select v-model="selected">
<option value="">Select</option>
<option value="option1">Option 1</option>
<option value="option2">Option 2</option>
</select>
</div>
<div>
<select v-model="multiSelect" multiple>
<option value="option1">Option 1</option>
<option value="option2">Option 2</option>
<option value="option3">Option 3</option>
</select>
</div>
<button type="submit" :disabled="!isValid">Submit</button>
</form>
</template>
表单验证
简单验证示例
vue
<script setup>
import { ref, computed } from 'vue'
const email = ref('')
const password = ref('')
const agree = ref(false)
const emailError = computed(() => {
if (!email.value) return 'Email is required'
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/
if (!emailRegex.test(email.value)) return 'Invalid email format'
return ''
})
const passwordError = computed(() => {
if (!password.value) return 'Password is required'
if (password.value.length < 6) return 'Password must be at least 6 characters'
return ''
})
const formValid = computed(() => {
return !emailError.value && !passwordError.value && agree.value
})
</script>
<template>
<form>
<div class="form-group">
<label for="email">Email:</label>
<input
id="email"
v-model="email"
type="email"
:class="{ error: emailError }"
placeholder="Enter your email"
>
<span v-if="emailError" class="error">{{ emailError }}</span>
</div>
<div class="form-group">
<label for="password">Password:</label>
<input
id="password"
v-model="password"
type="password"
:class="{ error: passwordError }"
placeholder="Enter your password"
>
<span v-if="passwordError" class="error">{{ passwordError }}</span>
</div>
<div class="form-group">
<label>
<input type="checkbox" v-model="agree">
I agree to the terms and conditions
</label>
</div>
<button type="button" :disabled="!formValid" @click="submitForm">
Submit
</button>
</form>
</template>
<style>
.form-group {
margin-bottom: 15px;
}
.error {
border: 1px solid red;
}
.error-message {
color: red;
font-size: 14px;
}
</style>
高级表单处理
动态表单
vue
<script setup>
import { ref, reactive } from 'vue'
const formData = reactive({
name: '',
email: '',
age: null,
gender: '',
skills: [],
bio: ''
})
const skillsOptions = ['JavaScript', 'Vue.js', 'React', 'Node.js', 'Python']
function submitForm() {
console.log('Form submitted:', { ...formData })
// 提交表单逻辑
}
function resetForm() {
Object.keys(formData).forEach(key => {
if (Array.isArray(formData[key])) {
formData[key] = []
} else {
formData[key] = typeof formData[key] === 'number' ? null : ''
}
})
}
</script>
<template>
<form @submit.prevent="submitForm">
<div class="form-row">
<label>Name:</label>
<input v-model="formData.name" type="text" placeholder="Your name">
</div>
<div class="form-row">
<label>Email:</label>
<input v-model="formData.email" type="email" placeholder="Your email">
</div>
<div class="form-row">
<label>Age:</label>
<input v-model.number="formData.age" type="number" placeholder="Your age">
</div>
<div class="form-row">
<label>Gender:</label>
<select v-model="formData.gender">
<option value="">Select</option>
<option value="male">Male</option>
<option value="female">Female</option>
<option value="other">Other</option>
</select>
</div>
<div class="form-row">
<label>Skills:</label>
<div class="checkbox-group">
<label v-for="skill in skillsOptions" :key="skill">
<input
type="checkbox"
:value="skill"
v-model="formData.skills"
>
{{ skill }}
</label>
</div>
</div>
<div class="form-row">
<label>Bio:</label>
<textarea
v-model="formData.bio"
placeholder="Tell us about yourself"
rows="4"
></textarea>
</div>
<div class="form-actions">
<button type="submit">Submit</button>
<button type="button" @click="resetForm">Reset</button>
</div>
</form>
</template>
<style>
.form-row {
margin-bottom: 15px;
}
.checkbox-group label {
display: inline-block;
margin-right: 15px;
}
.form-actions {
margin-top: 20px;
}
.form-actions button {
margin-right: 10px;
padding: 8px 16px;
}
</style>
最佳实践
1. 使用适当的输入类型
vue
<template>
<div>
<input type="email" v-model="email" placeholder="Email">
<input type="number" v-model.number="age" placeholder="Age">
<input type="tel" v-model="phone" placeholder="Phone">
<input type="url" v-model="website" placeholder="Website">
<input type="date" v-model="date" placeholder="Date">
</div>
</template>
2. 合理使用修饰符
vue
<template>
<div>
<!-- 自动转换为数字 -->
<input v-model.number="age" type="number">
<!-- 自动去除首尾空格 -->
<input v-model.trim="username" type="text">
<!-- 在change事件后更新,而不是input事件 -->
<input v-model.lazy="searchQuery" type="text">
</div>
</template>
3. 处理表单提交
vue
<script setup>
import { ref } from 'vue'
const form = ref({
email: '',
password: ''
})
const submitting = ref(false)
async function handleSubmit() {
if (!form.value.email || !form.value.password) {
alert('Please fill in all fields')
return
}
submitting.value = true
try {
// 模拟API调用
await new Promise(resolve => setTimeout(resolve, 1000))
console.log('Form submitted successfully')
} catch (error) {
console.error('Form submission failed:', error)
} finally {
submitting.value = false
}
}
</script>
<template>
<form @submit.prevent="handleSubmit">
<input
v-model="form.email"
type="email"
placeholder="Email"
required
>
<input
v-model="form.password"
type="password"
placeholder="Password"
required
>
<button type="submit" :disabled="submitting">
{{ submitting ? 'Submitting...' : 'Submit' }}
</button>
</form>
</template>
表单输入绑定是Vue.js中实现用户数据交互的重要功能,通过合理使用v-model指令及其修饰符,可以轻松实现双向数据绑定,构建出响应迅速、验证完善的表单界面。