SoFunction
Updated on 2025-04-04

Detailed explanation of the usage of v-model in vue3

Bind a single attribute

Basic binding

Custom componentsCustomInputGive an example

<script setup>
    const txt = ref('');
 </script>
 
 <template>
  <CustomInput v-model="txt" />
 </template>

v-modelWill be expanded into the following form

<CustomInput
  :modelValue="txt"
  @update:modelValue="newValue => txt = newValue"
/>

<CustomInput>There are two things that need to be done inside the component:

  • Make internal native<input>Elementalvalueattribute bind tomodelValue prop
  • When nativeinputWhen the event is triggered, trigger a new value.update:modelValueCustom events

Here is the corresponding code:

<script setup>
const props = defineProps({
  'modelValue': String,
})
const emit = defineEmits(["update:modelValue"])
</script>

<template>
    <input :value="modelValue" @input="$emit('update:modelValue', $)" />
</template>

Some people think this writing method is too cumbersome, which will make the tag code longer

Another implementation within the componentv-modelThe way is to use a writable   with both getter and settercomputedAttributes

computed binding

usecomputedWhen attributes,getThe method needs to be returnedmodelValueprop, andsetThe method needs to trigger the corresponding event

<script setup>
const value = computed({
  get() {
    return 
  },
  set(value) {
    emit("update:modelValue", value)
  }
})
</script>

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

This writing method can simplify the properties in the tag and have clear logic

A single attribute can be usedv-modelIt's easy to do it, what if multiple attributes require two-way binding?

🚀 v-model binds multiple attributes

By default,v-modelAll are used on componentsmodelValueAs a prop, andupdate:modelValueAs the corresponding event

But we can givev-modelSpecify a parameter to change these names:

<template>
    <CustomInput v-model:first-name="first" v-model:last-name="last" />
</template>

Similarly, it can be bound in two ways, justpropFrom the originalmodelValueIt becomes the passed parameter name, and the corresponding event also becomesupdate: parameter name

 &lt;script setup&gt;
 const props = defineProps({
  firstName: String,
  lastName: String,
})
// Use in computedconst emit = defineEmits(['update:firstName', 'update:lastName'])
&lt;/script&gt;

&lt;template&gt;
  &lt;input
    type="text"
    :value="firstName"
    @input="$emit('update:firstName', $)"
  /&gt;
  &lt;input
    type="text"
    :value="lastName"
    @input="$emit('update:lastName', $)"
  /&gt;
&lt;/template&gt;

🚩 Bind the object

In a complex component, if multiple fields require two-way binding, it will be a bit complicated if the method shown above is used.

Introducing two ways to bind objects from two directions

Define parent componentsearchBarFor a complex form component

&lt;script setup&gt;
import { ref } from "vue"

const modelValue = ref({
  keyword: "123",
  selectValue: "",
  options: [
    {
      label: "all",
      value: ""
    },
    {
      label: "a1",
      value: "1"
    },
    {
      label: "a2",
      value: "2"
    },
  ]
})
&lt;/script&gt;

&lt;template&gt;
    &lt;searchBar v-model="modelValue" /&gt;
&lt;/template&gt;

Then insearchBarIn the component, we receivemodelValueAnd define the type asObject

&lt;template&gt;
  &lt;div&gt;
    &lt;!-- &lt;input type="text" v-model=""&gt; Two-way binding can be achieved --&gt;
    &lt;input type="text" 
      :value=""
      @input="handleKeywordChange"
    &gt;
    &lt;select v-model=""&gt;
      &lt;option v-for="o in " :key="" :value=""&gt;
        {{  }}
      &lt;/option&gt;
    &lt;/select&gt;
  &lt;/div&gt;
&lt;/template&gt;

&lt;script lang="ts" setup&gt;

const props = defineProps({
  modelValue: {
    type: Object,
    default: () =&gt; ({})
  }
})

const emit = defineEmits(["update:modelValue"]);

// Take input as an exampleconst handleKeywordChange=(val)=&gt;{
  emit("update:modelValue",{
    ...,
    keyword:
  })
}
&lt;/script&gt;

If you pass in an object, as described in the comments
<input type="text" v-model="">Although two-way binding can be performed directly, this will destroySingle data flow

and aboveemitThe same thing as the event is triggered, but the passed data becomes an object

Although using emit can trigger two-way binding, it is too cumbersome. Here is a more elegant writing method, which can be said to be a strange trick --computed + prxoy

If usingcomputedBind, you might write this kind of code

&lt;template&gt;
      &lt;input type="text" v-model=""&gt;
 &lt;/template&gt;
 
&lt;script lang="ts" setup&gt;

const model = computed({
  get() {
    return 
  },
  set(value) {
    // (value) // found no print     emit("update:modelValue", {
      ...,
       keyword: value
     })
  }
})
&lt;script&gt;

But when you enter, you will find that it is not triggered.setter, becausecomputedWill be a first-level proxy, and the proxy object has not been modified

If you want to triggersetter, as shown below:

// Only in this way will change  = {
   keyword:"asdfad"
 }

This method cannot triggersetter, it cannot be bound in two directions. What should I do?

existgetterReturn oneAgent object!existgetterReturn oneAgent object!existgetterReturn oneAgent object!

becauseproxyThe proxy object is consistent with the proxy object attributes, so we useproxyPackage original object

Sov-modelThe object bound to the proxy is after the proxy. If the attributes of the proxy object change, the proxy object will be triggered.setMethod, at this time we can triggeremit

const model = computed({
  get() {
    return new Proxy(, {
      set(obj, name, val) {
        emit("update:modelValue", {
          ...obj,
          [name]: val
        })
        return true
      }
    })
  },
  set(value) {
    emit("update:modelValue", {
      ...,
      keyword: value
    })
  }
})

Modifier

We knowv-modelThere are some built-in modifiers, such as.trim.numberand.lazy

In some scenarios, we may want a custom componentv-modelSupports custom modifiers.

Let's create a custom modifiercapitalize, it will automaticallyv-modelThe first letter of the string value bound to be converted to capitalization:

  <CustomInput ="txt" />

We addedcapitalizeModifier, it will be automatically passed topropInmodelModifiersmiddle

&lt;script setup&gt;
const props = defineProps({
  modelValue: String,
  modelModifiers: {
    default: () =&gt; ({})
  }
})

const emitValue = (e) =&gt; {
  let value = ;
  // Use modifier  if () {
    value = (0).toUpperCase() + (1)
  }
  emit('update:modelValue', value)
}
&lt;/script&gt;

&lt;template&gt;
  &lt;input :value="modelValue" @input="emitValue" /&gt;
&lt;/template&gt;

Summarize

existvue, we can usev-modelThe instructions are very convenient for two-way binding, and can complete many interesting functions with modifiers.

The above is a detailed explanation of the usage of v-model in vue3. For more information about the usage of vue3 v-model, please follow my other related articles!