Vue2和Vue3的v-model实现对比显示:语法保持兼容但底层机制优化。
Vue3将默认属性名从value改为modelValue,事件名从input变为update:modelValue,并原生支持多v-model绑定(替代Vue2的.sync修饰符)。
自定义修饰符处理增强,通过modelModifiers访问。
迁移时需调整prop和事件命名,多绑定改用v-model:propName形式。
Vue3的改进使表单绑定更灵活直观,同时保持与Vue2的语法兼容性。
Vue3对v-model机制进行了优化升级。Vue3的改进包括支持类型检查、多绑定和增强修饰符处理,提升了开发体验。
v-model 在 Vue 2 和 Vue 3 中的实现对比
| 对比维度 | Vue 2 | Vue 3 |
|---|---|---|
| 基本语法 | v-model="value" |
v-model="value" (语法不变) |
| 底层原理 | v-model = :value + @input |
v-model = :modelValue + @update:modelValue |
| 组件上的默认属性名 | value |
modelValue |
| 组件上的默认事件名 | input |
update:modelValue |
| 多 v-model 绑定 | 不支持,需使用 .sync 修饰符 |
原生支持:v-model:propName="value" |
| 自定义修饰符 | 有限支持 | 增强支持,可通过 modelModifiers 访问 |
| 修饰符示例 | .lazy, .number, .trim (内置) |
保留内置修饰符,并支持自定义修饰符处理 |
| 组件内实现 | 通过 model 选项配置 prop 和 event |
通过 props 和 emits 声明,默认使用 modelValue |
| 示例代码 | vue <ChildComponent v-model="pageTitle" /> |
vue <ChildComponent v-model="pageTitle" /> |
| 示例代码(组件内) | js model: { prop: 'title', event: 'change' }, props: ['title'] |
js props: ['modelValue'], emits: ['update:modelValue'] |
| 多绑定示例 | vue <ChildComponent :title.sync="pageTitle" :content.sync="pageContent" /> |
vue <ChildComponent v-model:title="pageTitle" v-model:content="pageContent" /> |
| 自定义修饰符处理 | 较复杂,需通过计算属性等方法 | 通过 modelModifiers prop 访问,如 props.modelModifiers.capitalize |
关键变化总结
-
兼容性 :Vue 3 中
v-model的语法与 Vue 2 保持基本一致,确保迁移的平顺性 -
默认属性/事件名变更 :从
value/input变为modelValue/update:modelValue -
多 v-model 支持 :Vue 3 原生支持多个
v-model绑定,替代 Vue 2 的.sync修饰符 -
自定义修饰符增强:Vue 3 提供了更直观的方式处理自定义修饰符
-
.sync修饰符 :Vue 2 的.sync修饰符在 Vue 3 中被移除,其功能由多v-model语法替代
迁移建议
-
Vue 2 组件使用
v-model时,在 Vue 3 中需要将valueprop 改为modelValue,将input事件改为update:modelValue -
Vue 2 中使用
.sync修饰符的地方,在 Vue 3 中应改为v-model:propName形式 -
自定义修饰符的处理逻辑需要适应 Vue 3 的新 API
Vue 2 确实默认每个组件只能有一个 v-model
但有两种方式可以实现多个双向数据绑定:
1. 默认的单个 v-model(2.2.0+)
默认情况下,组件的 v-model 会使用 value 属性和 input 事件:
vue
<!-- 父组件 -->
<ChildComponent v-model="pageTitle" />
<!-- 相当于 -->
<ChildComponent :value="pageTitle" @input="pageTitle = $event" />
<!-- 子组件 -->
<script>
export default {
props: ['value'],
methods: {
updateValue(newValue) {
this.$emit('input', newValue)
}
}
}
</script>
2. 实现多个 "v-model" 的两种方式
方式一:使用 .sync 修饰符(推荐)
.sync 修饰符可以实现多个双向绑定:
vue
<!-- 父组件 -->
<ChildComponent
:title.sync="pageTitle"
:content.sync="pageContent"
/>
<!-- 子组件 -->
<script>
export default {
props: ['title', 'content'],
methods: {
updateTitle(newTitle) {
this.$emit('update:title', newTitle)
},
updateContent(newContent) {
this.$emit('update:content', newContent)
}
}
}
</script>
方式二:自定义 v-model(不常用)
可以在组件中配置 model 选项来修改默认的 prop 和 event:
javascript
// 子组件
export default {
model: {
prop: 'checked',
event: 'change'
},
props: ['checked']
}
3. Vue 3 的改进
Vue 3 原生支持多个 v-model:
vue
<!-- Vue 3 -->
<UserName
v-model:first-name="firstName"
v-model:last-name="lastName"
/>
总结
虽然 Vue 2 默认只支持一个 v-model,但通过 .sync 修饰符可以轻松实现多个双向绑定。在实际开发中,.sync 是处理多个双向数据绑定的标准做法。
Vue3 modelValue onUpdate:modelValue 使用示例
在 Vue 3 中,modelValue 是默认的 v-model prop 名称,update:modelValue 是默认的事件名称。
1. 基础使用示例
子组件:CustomInput.vue
vue
<template>
<div>
<input
:value="modelValue"
@input="$emit('update:modelValue', $event.target.value)"
/>
<p>当前值:{{ modelValue }}</p>
</div>
</template>
<script setup>
// 使用 <script setup>
const props = defineProps(['modelValue'])
const emit = defineEmits(['update:modelValue'])
</script>
父组件使用:
vue
<template>
<CustomInput v-model="text" />
<p>父组件值:{{ text }}</p>
</template>
<script setup>
import { ref } from 'vue'
import CustomInput from './CustomInput.vue'
const text = ref('')
</script>
2. 使用 Options API 的示例
子组件:
vue
<template>
<input
:value="modelValue"
@input="handleInput"
/>
</template>
<script>
export default {
name: 'CustomInput',
props: ['modelValue'],
emits: ['update:modelValue'],
methods: {
handleInput(event) {
this.$emit('update:modelValue', event.target.value)
}
}
}
</script>
3. 带类型定义和验证的示例
vue
<template>
<div>
<input
type="number"
:value="modelValue"
@input="handleInput"
:min="min"
:max="max"
/>
</div>
</template>
<script setup>
import { defineProps, defineEmits } from 'vue'
const props = defineProps({
modelValue: {
type: Number,
required: true,
default: 0
},
min: {
type: Number,
default: 0
},
max: {
type: Number,
default: 100
}
})
const emit = defineEmits(['update:modelValue'])
const handleInput = (event) => {
let value = parseInt(event.target.value) || 0
// 限制范围
if (value < props.min) value = props.min
if (value > props.max) value = props.max
emit('update:modelValue', value)
}
</script>
4. 多个 v-model 绑定示例
子组件:UserForm.vue
vue
<template>
<div>
<input
:value="firstName"
@input="$emit('update:firstName', $event.target.value)"
placeholder="First Name"
/>
<input
:value="lastName"
@input="$emit('update:lastName', $event.target.value)"
placeholder="Last Name"
/>
</div>
</template>
<script setup>
defineProps({
firstName: String,
lastName: String
})
defineEmits(['update:firstName', 'update:lastName'])
</script>
父组件使用多个 v-model:
vue
<template>
<UserForm
v-model:firstName="firstName"
v-model:lastName="lastName"
/>
<p>全名:{{ firstName }} {{ lastName }}</p>
</template>
<script setup>
import { ref } from 'vue'
import UserForm from './UserForm.vue'
const firstName = ref('')
const lastName = ref('')
</script>
5. 带修饰符的 v-model
子组件:TrimInput.vue
vue
<template>
<input
:value="modelValue"
@input="handleInput"
/>
</template>
<script setup>
import { defineProps, defineEmits } from 'vue'
const props = defineProps({
modelValue: String,
modelModifiers: { // 自动接收修饰符
default: () => ({})
}
})
const emit = defineEmits(['update:modelValue'])
const handleInput = (event) => {
let value = event.target.value
// 处理修饰符
if (props.modelModifiers.trim) {
value = value.trim()
}
if (props.modelModifiers.capitalize) {
value = value.charAt(0).toUpperCase() + value.slice(1)
}
emit('update:modelValue', value)
}
</script>
父组件使用修饰符:
vue
<template>
<TrimInput v-model.trim="text1" />
<TrimInput v-model.trim.capitalize="text2" />
</template>
<script setup>
import { ref } from 'vue'
import TrimInput from './TrimInput.vue'
const text1 = ref('')
const text2 = ref('')
</script>
6. 使用 computed 实现双向绑定的更复杂示例
vue
<template>
<div>
<input
:value="displayValue"
@input="updateValue($event.target.value)"
/>
</div>
</template>
<script setup>
import { computed, defineProps, defineEmits } from 'vue'
const props = defineProps({
modelValue: String
})
const emit = defineEmits(['update:modelValue'])
// 使用 computed 处理显示值
const displayValue = computed({
get() {
return props.modelValue || ''
},
set(value) {
emit('update:modelValue', value)
}
})
// 或者直接使用方法
const updateValue = (value) => {
// 可以在这里添加自定义逻辑
const processedValue = value.toUpperCase() // 示例:转为大写
emit('update:modelValue', processedValue)
}
</script>
关键点总结:
-
默认 prop :
modelValue -
默认事件 :
update:modelValue -
语法糖简化:
vue
<Child v-model="data" /> <!-- 等价于 --> <Child :modelValue="data" @update:modelValue="data = $event" /> -
多个 v-model : 使用
v-model:propName语法 -
修饰符 : 通过
modelModifiersprop 接收
这种设计让 Vue 3 的双向绑定更加灵活和类型安全。