一、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 是 valuecomputed: {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'], // 接收父组件传递的 titlecomputed: {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 默认 propemits: ['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 中扩展逻辑。