SoFunction
Updated on 2025-04-14

One article will give you an in-depth understanding of V-model's implementation of two-way data binding

Parent component implements two-way binding

Use v-model in parent component:

<ChildComponent v-model="message" />

Equivalent to:

<ChildComponent :modelValue="message" @update:modelValue="val => message = val" />

:modelValue: Pass the value of message to the child component through props.

@update:modelValue: Send update value to the parent component via emit.

Subcomponents implement bidirectional binding

In order to support the v-model of the parent component, the child component needs:

1. Define props to receive data passed by the parent component.

2. Use emit to send update events.

&lt;script setup&gt;
import { defineProps, defineEmits } from 'vue';

// Receive the modelValue passed by the parent componentdefineProps({
  modelValue: String, // The value passed by the parent component});

// Define update eventdefineEmits(['update:modelValue']);

const updateValue = (newValue) =&gt; {
  // Trigger event to notify the parent component to update data  emit('update:modelValue', newValue);
};
&lt;/script&gt;

&lt;template&gt;
  &lt;input :value="modelValue" @input="updateValue($)" /&gt;
&lt;/template&gt;

defineModel()

Starting from Vue 3.4, the recommended implementation is to use the defineModel() macro

<!--  -->
<script setup>
const model = defineModel()

function update() {
  ++
}
</script>

<template>
  <div>Parent bound v-model is: {{ model }}</div>
  <button @click="update">Increment</button>
</template>

The parent component can use v-model to bind a value:

template

<!--  -->
<Child v-model="countModel" />

defineModel() returns a ref. It can be accessed and modified like other refs, but it can function as a two-way binding between the parent component and the current variable:

  • Its .value is synchronized with the value of the v-model of the parent component;
  • When it is changed by the child component, the value bound by the parent component will be triggered to be updated together.

This means you can also use v-model to bind this ref to a native input element, and easily wrap the native input element while providing the same v-model usage:

<script setup>
const model = defineModel()
</script>

<template>
  <input v-model="model" />
</template>

The underlying mechanism

defineModel is a convenience macro. The compiler expands it to the following:

A prop named modelValue, with the value of the local ref synchronized;

An event named update:modelValue is triggered when the value of the local ref changes.

This article about this article about V-model implementing bidirectional binding of data is introduced here. For more related bidirectional binding of V-model data, please search for my previous articles or continue browsing the related articles below. I hope everyone will support me in the future!