Vue.js - 自定义组件实现v-model双向绑定详解
作者:hangge | 2026-04-15 10:26
我们知道 v-model 指令可以方便地实现表单元素数据的双向绑定,该指令的实现原理其实是 v-bind 和 v-on 这两个指令:
- v-bind 指令会将表单元素的 value 属性与一个变量绑定。
- v-on 指令会绑定 input 事件,并在事件回调中重新为 value 属性绑定的变量赋值。
如果我们想在自定义上使用 v-model 指令实现数据双向绑定也是同样的原理。特别注意的是:
- Vue 2 组件和 <input> 一样,默认绑定的值是 value,默认监听事件是 input。
- Vue 3 组件中默认绑定的值不是 value,而是 modelValue;默认监听的事件也不是 input,而是 update:model-value。
1,v-model 的基本使用方法
(1)首先我们创建一个自定义的 HyInput.vue 组件,Vue 3 代码如下:
- 该组件中 <input> 元素的 value 属性绑定了 props 中的 modelValue 属性。
- 当 <input> 元素监听到有输入时,会触发 inputClick 函数回调,该函数会向父组件触发 update:modelValue 事件,并将 <input> 元素输入的内容通过 $emit 函数的第二个参数传递给父组件。
<template>
<div class="hy-input">
HyInput组件:
<input :value="modelValue" @input="inputClick">
</div>
</template>
<script>
export default {
props: {
modelValue: String
},
emits: ["update:modelValue"],
methods: {
inputClick(event) {
this.$emit("update:modelValue", event.target.value);
}
}
}
</script>
- 如果是 Vue 2,则该组件代码如下:
<template>
<div class="hy-input">
HyInput组件:
<input :value="value" @input="inputClick">
</div>
</template>
<script>
export default {
props: {
value: String
},
emits: ["input"],
methods: {
inputClick(event) {
this.$emit("input", event.target.value);
}
}
}
</script>
(2)然后我们在 App.vue 上使用该组件。首先将 message 变量绑定到 <h4> 标签上进行显示,同时通过单击 <button> 修改 message 变量。接着在 <hy-input> 组件上使用 v-model 指令双向绑定 message 变量。
<template>
<div class="app">
<h4>App组件message变量:{{ message }}</h4>
<button @click="changeMessage">App组件修改message</button>
<!-- 组件上使用v-model -->
<hy-input v-model="message"></hy-input>
</div>
</template>
<script>
import HyInput from './HyInput.vue';
export default {
components: {
HyInput
},
data() {
return {
message: "hangge.com",
}
},
methods: {
changeMessage() {
this.message = "航歌"
}
}
}
</script>
(3)运行效果如下,当在 Hylnput.vue 组件的 <input> 元素中输入内容时,App.vue 组件的 message 也会随之发生改变。当单击“App 组件修改 message”按钮修改 App.vue 组件的 message 时,Hylnput.vue 组件的 <input> 元素输入框的值也会随之改变。这样,我们就成功地在组件中使用 v-model 指令实现了数据双向绑定。

2,v-model 绑定 computed
(1)上面样例中,HyInput.vue 组件中的 <input:value="modelValue"@input="inputClick"> 不能简写为 <input v-model="modelValue">。这是因为在组件内部修改了 props 后,外部并不知道 props 已经被修改,因此不会将事件传递出去,即不会触发 update:modelValue 事件。另外,在开发过程中直接修改 props 中的属性是一个不好的习惯,不符合单向数据流的原则。
(2)如果仍然希望在 Hylnput.vue 组件中支持类似 <input v-model="xxxx"> 的写法,可以借助计算属性的 setter 和 getter 来实现。 修改 HyInput.vue 子组件,将计算属性 value 双向绑定到 <input> 元素上,而不是 props 的 modelValue:
- 当 modelValue 属性发生改变时,会触发 value 计算属性的 getter 方法,将其值绑定到 <input> 元素上。
- 当用户在 <input> 中输入内容时,会触发 value 计算属性的 setter 方法,该方法会触发 update:modelValue 事件,通知父组件更新 message 属性。
<template>
<div class="hy-input">
HyInput组件:
<!-- 绑定到value计数属性- -->
<input v-model="value">
</div>
</template>
<script>
export default {
props: {
modelValue: String
},
emits: ["update:modelValue"],
computed: {
value: {
set(value) {
this.$emit("update:modelValue", value);
},
get() {
return this.modelValue;
}
}
},
}
</script>
(3)保存代码,这次 HyInput.vue 组件 v-model 指令依然可以实现数据的双向绑定。

3,组件上应用多个 v-model
(1)默认情况下,v-model 绑定了 modelValue 属性和 @update:modelValue 事件。实际上,v-model 指令支持传入一个参数,可以通过这个参数指定需要绑定的属性和事件名称,这样就可以在一个组件中应用多个 v-model 指令了。
(2)我们新建 HyMultiplelnput.vue 组件,并在该组件中将 props 中的 modelValue 和 title 属性双向绑定到各自的 <input> 元素上,代码如下:
<template>
<div class="hy-multiple-input">
HyMultipleInput组件:
<input :value="modelValue" @input="input1Change">
<input :value="title" @input="input2Change">
</div>
</template>
<script>
export default {
props: {
modelValue: String,
title: String
},
emits: ["update:modelValue", "update:title"],
methods: {
input1Change(event) {
this.$emit("update:modelValue", event.target.value);
},
input2Change(event) {
this.$emit("update:title", event.target.value);
}
}
}
</script>
<style scoped>
.hy-multiple-input {
border: 1px solid #999;
margin: 15px;
padding: 5px;
}
</style>
(3)App.vue 组件中使用 HyMultiplelnput.vue 组件,代码如下。可以看到,该组件上使用两个 v-model 指令分别绑定了 message 和 title 变量。其中,v-model:title 相当于做了两件事: 绑定 title 属性和监听 @update:title 事件。
<template>
<div class="app">
<h4>App组件message变量:{{ message }}</h4>
<h4>App组件title变量:{{ title }}</h4>
<button @click="changeMessage">App组件修改message和title</button>
<!-- 绑定两个v-model -->
<hy-multiple-input v-model="message" v-model:title="title"></hy-multiple-input>
</div>
</template>
<script>
import HyMultipleInput from './HyMultipleInput.vue';
export default {
components: {
HyMultipleInput
},
data() {
return {
message: "hangge.com",
title: "hello"
}
},
methods: {
changeMessage() {
this.message = "航歌"
this.title = "你好"
}
}
}
</script>
(4)运行效果如下,可以看到在 <hy-multiple-input> 组件上使用两个 v-model 指令依然可以实现数据的双向绑定。

4,v-model 绑定对象类型
(1)前面样例 v-model 绑定的数据都是字符串或数字类型的。下面演示如何使用 v-model 指令绑定对象类型的数据。这里我们创建一个 HyForm.vue 组件,代码如下所示:
- 首先分别为 <input> 元素的 value 绑定了 modelValue 对象中的 name 和 title 属性
- 接着分别监听 <input> 元素的输入,当 <input> 元素有输入时,回调 inputChange 函数,该函数会接收两个参数:事件对象和当前绑定对象属性的名称。
- 然后在该函数中触发 update:modelValue 事件,并将 this.modelValue 的内容和 <input> 元素输入的内容进行合并生成新的对象。最后把该对象作为 $emit 函数的第二个参数传递给父组件。
<template>
<div class="hy-multiple-form">
HyFrom组件:
<input :value="modelValue.name" @input="inputChange($event, 'name')">
<input :value="modelValue.title" @input="inputChange($event, 'title')">
</div>
</template>
<script>
export default {
props: {
modelValue: Object // 对象
},
emits: ["update:modelValue"],
methods: {
inputChange(event, field) {
this.$emit("update:modelValue", {
...this.modelValue,
[field]: event.target.value,
});
}
}
}
</script>
(2)App.vue 中添加 HyForm.vue 组件的使用,代码如下:
<template>
<div class="app">
<h5>App组件formData.name::{{ formData.name }}</h5>
<h5>App组件formData.title:{{ formData.title }}</h5>
<button @click="changeFormData">App组件修改对象</button>
<!-- v-model绑定对象 -->
<hy-form v-model="formData"></hy-form>
</div>
</template>
<script>
import HyForm from './HyForm.vue';
export default {
components: {
HyForm
},
data() {
return {
formData: {
name: 'hangge',
title: 'hello'
}
}
},
methods: {
changeFormData() {
this.formData.name = "航歌"
this.formData.title = "你好"
}
}
}
</script>
(3)运行效果如下,可以看到在 HyForm.vue 组件中使用 v-model 绑定对象类型,也可以实现数据的双向绑定。

全部评论(0)