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 defaultvalue
Prop and namedinput
The events; - when
<input type="checkbox"> checkbox
和<input type="radio"> Click
The default binding name ischecked
Prop and namedchange
The events; - when
The < select > select box
When used on, the binding namevalue
Prop and namedchange
In 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.