The V-Model is one of the most frequently used instructions in Vue, and the V-Model in Vue3 has changed a lot. This article will discuss the differences between THE V-Model in Vue2 and Vue3 in detail.

Vue2 v – in the model

If you are familiar with the syntax in Vue2, skip this section.

Let’s start by reviewing the V-Model in Vue2, which is primarily used for form elements and custom components. The V-Model is essentially a syntax sugar that does some special processing to the user’s input to update the data, and that processing is essentially a default binding of attributes and events to the elements used.

When v-Models are used on form elements, they are treated differently depending on the element:

  • when< input type = "text" > text<textarea>When used on, the element’s binding name is given by defaultvalueProp and namedinputThe events;
  • when<input type="checkbox"> checkbox<input type="radio"> ClickThe default binding name ischeckedProp and namedchangeThe events;
  • whenThe < select > select boxWhen used on, the binding namevalueProp and namedchangeIn the event.

These are handled by Vue by default and can be used directly. But you’ll also find third-party components that can use v-Models, such as the Input component in Element. This is because the components themselves implement the V-Model, which is essentially the binding properties and events described above.

We can try implementing the V-Model to develop a simple input component called MyInput:

<! MyInput component code -->

<template>
  <div>
    <input type="text" :value="value" @input="$emit('input',$event.target.value)">
  </div>
</template>

<script>
export default {
  props: {
    value: String.// Accept a prop named value by default}}</script>
Copy the code

This code implements the component’s V-Model functionality when using v-Model on this component:

<my-input v-model="msg"></my-input>
Copy the code

In fact, it is equivalent to:

<my-input :value="msg" @input="msg = $event">
Copy the code

Vue also provides the Model option to change the property or event name to something else, such as the MyInput component above, which we changed:

<template>
  <div>
    <input
      type="text"
      :value="title"
      @input="$emit('change', $event.target.value)"
    />
  </div>
</template>

<script>
export default {
  model: {
    prop: "title".// Change the default prop name value to title
    event: "change".// Change the default event name input to change
  },
  props: {
    title: String.// Notice that the template code should also be changed to title}};</script>
Copy the code

Use components at this point:

<my-input v-model="msg"></my-input>/ / is equivalent to<my-input :title="msg" @change="msg = $event"></my-input>
Copy the code

Use the.sync modifier

Vue provides a.sync modifier that, like v-model, automatically updates the parent component’s data when the child component’s data changes. The way to implement.sync is similar to the way to implement v-Model, except that the event name thrown should be update:myPropName.

Using MyInput, we pass in a title prop and throw an update:title event inside the component as follows:

// Change the name of the event thrown in MyInput to update:title<input type="text" :value="title" @input="$emit('update:title', $event.target.value)" />
Copy the code

If you use this component, it should look like this:

<my-input :title="msg" @update:title="msg = $event"></my-input>
Copy the code

But we can simplify this by using the.sync modifier:

<my-input :title.sync="msg"></my-input>
Copy the code

As you can see,.sync and v-Model achieve the same effect, depending on your scenario, as v-Model is used on form components.

Vue3 v – in the model

The main changes in Vue3 are as follows:

Modify the default prop name and event name

When used on custom components, the prop name of the V-Model default binding changes from Value to modelValue, and the event name changes from the default input to Update :modelValue. When writing the MyInput component above in Vue3, this is what you need:

<! MyInput Vue3 -->

<template>
  <div>
    <input
      type="text"
      :value="modelValue"
      @input="$emit('update:modelValue', $event.target.value)"// Change the event name toupdate:modelValue
    />
  </div>
</template>

<script>
export default {
  props: {
    modelValue: String.// Change the default prop from value to modelValue}};</script>
Copy the code

When using components:

<my-input v-model="msg"></my-input>/ / is equivalent to<my-input :modelValue="msg" @update:modelValue="msg = $event"></my-input>
Copy the code

Do away with the model option and the.sync modifier

Vue3 has removed the Model option so that the default prop name cannot be changed within components. There is now an easier way to pass the name of the prop to be modified directly after the V-Model:

// To change the default prop name, simply insert propName after v-model, for example, to title<my-input v-model:title="msg"></my-input>/ / is equivalent to<my-input :title="msg" @update:title="msg = $event"></my-input>
Copy the code

Note that internal components also need to be modified:

<template>
  <div>
    <input
      type="text"
      :value="title"
      @input="$emit('update:title', $event.target.value)"
    />
  </div>
</template>

<script>
export default {
  // The model option is no longer needed here
  props: {
    title: String.// Change it to title. Note that template also needs to be changed}};</script>
Copy the code

The.sync modifier has also been removed. If you try to use it, you will get an error like this:

‘.sync’ modifier on ‘v-bind’ directive is deprecated. Use ‘v-model:propName’ instead

As indicated in the error, you can use v-model:propName instead of.sync because the effect is essentially the same.

Use multiple V-Models

Vue3 supports the use of multiple V-Models, which is a new feature. I like this feature, which makes component data update more flexible. For example, if you have a form child component where multiple data entered by the user needs to be updated to display in the parent component, you might write:

<! -- Form child component Form

<template>
  <div class="form">
    
    <label for="name">The name</label>
    <input id="name" type="text" :value="name" @input="$emit('update:name',$event.target.value)">
    
    <label for="address">address</label>
    <input id="address" type="text" :value="address" @input="$emit('update:address',$event.target.value)">
  
  </div>
</template>

<script>
export default {
  props: {name: String.address: String}}</script>
Copy the code

When the parent uses this component:

<child-component v-model:name="name" v-model:address="address"></child-component>// Update user input data to display in the parent component<p>{{name}}</p>
<p>{{address}}</p>
Copy the code

Custom V-Model modifiers

On the V-Model in Vue2, we used.trim,.lazy, and.number as built-in modifiers, but Vue3 adds custom modifiers that developers can customize to handle binding values as needed.

When we add custom modifiers to the V-Model, they are passed to child components through a prop called the modelModifiers, which take the modifier name and modify the binding values based on the conditions. Let’s look at an example of customizing a capitalize modifier that capitalizes the first letter of an input string.

Assuming the custom component is still called MyInput, use the V-Model with the custom capitalize modifier:

<my-input v-model.capitalize="msg"></my-input>
Copy the code

Since it is not a built-in modifier, we need to handle the modifier logic inside the component ourselves, writing the component:

<! -- MyInput component -->

<template>
  <div>
    <input type="text" :value="modelValue" @input="emitValue" />
  </div>
</template>

<script>
export default {
  props: {
    modelValue: String.modelModifiers: {  // Custom modifiers are passed into this prop by default
      type: Object.default: () = >({})}},mounted() {
    // When custom modifiers are appended to a component V-model, the modifier status is obtained internally by the component modelModifiers
    console.log(this.modelModifiers); // {capitalize: true}
  },
  methods: {
    emitValue(e) {
      let value = e.target.value;
      // If a custom modifier is used, i.e. the state is true, the value is processed
      if (this.modelModifiers.capitalize) {
        value = value.charAt(0).toUpperCase() + value.slice(1);
      }
      // emit value
      this.$emit("update:modelValue", value); ,}}};</script>
Copy the code

This completes a V-Model modifier that capitalizes the first letter of the input string.

If the V-Model takes parameters and uses custom modifiers, for example:

<my-input v-model:title.capitalize="msg"></my-input>
Copy the code

A prop passed inside a component is no longer a modelModifiers, but a titleModifiers. Its format is ARG + ‘Modifiers’. The component should be written like this:

<! -- MyInput component -->

<template>
  <div>
    <input type="text" :value="title" @input="emitValue" />
  </div>
</template>

<script>
export default {
  props: {
    title: String.// modelValue -> title
    titleModifiers: {  // modelModifiers -> titleModifiers
      type: Object.default: () = >({})}},mounted() {
    console.log(this.titleModifiers); // {capitalize: true}
  },
  methods: {
    emitValue(e) {
      let value = e.target.value;

      // If custom modifiers are used, the values are processed
      if (this.titleModifiers.capitalize) {
        value = value.charAt(0).toUpperCase() + value.slice(1);
      }
      // emit value
      this.$emit("update:title", value); ,}}};</script>
Copy the code

conclusion

This is the difference between Vue2 and Vue3, and I personally think the logic is clearer in the new version. However, some parts are no longer compatible. If you are porting the Vue2 version of the code directly to a Vue3 project, you need to be aware of the implementation differences of the V-model, as well as the replacement of the.sync modifier with the new one.

If there is any improper place in the article, welcome to point out the comment section.