返回 导航

Vue.js

hangge.com

Vue.js - 自定义组件实现v-model双向绑定详解

作者:hangge | 2026-04-15 10:26
    我们知道 v-model 指令可以方便地实现表单元素数据的双向绑定,该指令的实现原理其实是 v-bindv-on 这两个指令:
  • v-bind 指令会将表单元素的 value 属性与一个变量绑定。 
  • v-on 指令会绑定 input 事件,并在事件回调中重新为 value 属性绑定的变量赋值。
    如果我们想在自定义上使用 v-model 指令实现数据双向绑定也是同样的原理。特别注意的是:
  • Vue 2 组件和 <input> 一样,默认绑定的值是 value,默认监听事件是 input
  • Vue 3 组件中默认绑定的值不是 value,而是 modelValue;默认监听的事件也不是 input,而是 update:model-value
    下面我将详细介绍一下在组件中使用 v-model 的方法。 

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>
<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"> 的写法,可以借助计算属性的 settergetter 来实现。 修改 HyInput.vue 子组件,将计算属性 value 双向绑定到 <input> 元素上,而不是 propsmodelValue
  • 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 中的 modelValuetitle 属性双向绑定到各自的 <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 指令分别绑定了 messagetitle 变量。其中,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 对象中的 nametitle 属性
  • 接着分别监听 <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)

回到顶部