一、Vue 2 中的实现
1. 使用 v-model
(默认 value
+ input
)
子组件 CustomInput.vue
<template>
<input v-model="internalValue" />
</template>
<script>
export default {
props: ['value'], // Vue 2 的 v-model 默认 prop 是 value
computed: {
internalValue: {
get() {
return this.value;
},
set(val) {
this.$emit('input', val); // Vue 2 的默认事件是 input
}
}
}
}
</script>
父组件
<template>
<CustomInput v-model="message" />
<p>父组件数据:{{ message }}</p>
</template>
<script>
export default {
data() {
return {
message: 'Hello Vue 2!'
}
}
}
</script>
2. 使用 .sync
修饰符(多属性双向绑定)
子组件
<template>
<input v-model="internalTitle" v-model="internalNums"/>
</template>
<script>
export default {
props: ['title','nums'], // 接收父组件传递的 title
computed: {
internalTitle: {
get() {
return this.title;
},
set(val) {
this.$emit('update:title', val); // 触发 update:title 事件
}
},
internalNums: {
get() {
return this.nums;
},
set(val) {
this.$emit('update:nums', val); // 触发 update:nums 事件
}
}
}
}
</script>
父组件
<template>
<CustomInput :title.sync="pageTitle" :nums.sync="pageNums"/>
<p>当前标题:{{ pageTitle }}</p>
<p>当前nums:{{ pageNums }}</p>
</template>
<script>
export default {
data() {
return {
pageTitle: '默认标题',
pageNums:'默认Nums',
}
}
}
</script>
二、Vue 3 中的实现
1. 默认 v-model
(modelValue
+ update:modelValue
)
子组件 CustomInput.vue
<script>
export default {
props: ['modelValue'], // Vue 3 的 v-model 默认 prop
emits: ['update:modelValue'], // 显式声明事件
computed: {
value: {
get() {
return this.modelValue;
},
set(val) {
this.$emit('update:modelValue', val);
}
}
}
}
</script>
<template>
<input v-model="value" />
</template>
父组件
<template>
<CustomInput v-model="message" />
<p>父组件数据:{{ message }}</p>
</template>
<script setup>
import { ref } from 'vue';
const message = ref('Hello Vue 3!');
</script>
2. 多 v-model
绑定(如 v-model:title
)
子组件
<template>
<input v-model="internalTitle" v-model="internalNums" />
</template>
<script>
export default {
props: ['title'],
emits: ['update:title','update:nums'],
computed: {
internalTitle: {
get() {
return this.title;
},
set(val) {
this.$emit('update:title', val);
}
},
internalNums: {
get() {
return this.nums;
},
set(val) {
this.$emit('update:nums', val);
}
}
}
}
</script>
父组件
<template>
<CustomInput v-model:title="pageTitle" v-model:nums="pageNums"/>
<p>当前标题:{{ pageTitle }}</p>
<p>当前标题:{{ pageNums }}</p>
</template>
<script setup>
import { ref } from 'vue';
const pageTitle = ref('默认title');
const pageNums = ref('默认nums');
</script>
三、关键区别总结
特性 | Vue 2 | Vue 3 |
---|---|---|
默认 prop 名 | value |
modelValue |
默认事件名 | input |
update:modelValue |
多属性双向绑定 | 需用 .sync 修饰符 |
直接 v-model:arg (如 v-model:title ) |
事件声明 | 无 emits 选项 |
需显式声明 emits |
Composition API | 不支持 <script setup> |
支持 <script setup> 简化写法 |
四、通用最佳实践
始终通过
$emit
更新数据
禁止在子组件中直接修改 prop(违反单向数据流)。复杂数据验证
在计算属性的setter
中添加校验逻辑:set(val) { if (val.length <= 100) { // 示例:输入长度限制 this.$emit('update:modelValue', val); } }
TypeScript 增强(Vue 3)
<script setup lang="ts"> const props = defineProps<{ modelValue: string }>(); const emit = defineEmits<{ (e: 'update:modelValue', value: string): void }>(); const value = computed({ get: () => props.modelValue, set: (val) => emit('update:modelValue', val) }); </script>
五、常见问题
Q:为什么 Vue 3 改用 modelValue
?
答案:避免与原生 HTML 元素的
value
属性冲突,同时支持多v-model
绑定。
Q:Vue 2 能兼容 Vue 3 的写法吗?
答案:部分兼容(Vue 2.7+ 支持
emits
声明但不强制),但默认行为仍需遵循 Vue 2 规则。
通过以上对比,你可以根据项目使用的 Vue 版本选择对应的实现方式。如果需要更复杂的场景(如防抖、格式化),可以在计算属性的 setter
中扩展逻辑。