On v-model and sync modifiers in Vue3

Many students'first impression of Vue is "responsive", "bi-directional binding" and other characteristics, while v-model is the grammatical sugar to achieve bi-directional binding, little partners who have used Vue must be very familiar with it. However, in Vue3, some changes have been made to the v-model so that it is no longer compatible with the usage of Vue2. Let's see what it is.

Incompatible: When used to customize components, the v-model prop and event default names have changed:

  • prop: value -> modelValueï¼›
  • Event: input -> update: modelValue;

Incompatible: v-bind's. sync modifier and component's model option have been removed, and a parameter can be added to v-model instead;

New: Multiple v-model bindings can now be used on the same component;
New: You can now customize the v-model modifier.

2.x Syntax

In 2.x, using v-model on a component is equivalent to binding value prop and triggering an input event:

<ChildComponent v-model="pageTitle" />

<!-- Is the following abbreviation: -->

<ChildComponent :value="pageTitle" @input="pageTitle = $event" />

Here the v-model is actually customized for form elements, and both input events and value prop are strongly coupled

If you want to change the prop or event name, you need to add the model option to the ChildComponent component:

<!-- ParentComponent.vue -->

<ChildComponent v-model="pageTitle" />
// ChildComponent.vue

export default {
  model: {
    prop: 'title',
    event: 'change'
  },
  props: {
    // This will allow the `value` attribute to be used for other purposes
    value: String,
    // Use `title` instead of `value` as prop for the model
    title: {
      type: String,
      default: 'Default title'
    }
  }
}

So, in this example, v-model is short for:

<ChildComponent :title="pageTitle" @change="pageTitle = $event" />

In some cases, we may need to "bind two-way" to a prop, such as the display hiding of a pop-up component, which can be controlled either from outside the component or from inside the component. For this reason, we recommend throwing events using update:myPropName. For example, for the HildComponent with title prop in the previous example, we could convey the intent to assign a new value to the parent in the following way:

this.$emit('update:title', newValue)

The parent component can then listen for the event when needed and update the local data property. For example:

<ChildComponent :title="pageTitle" @update:title="pageTitle = $event" />

For convenience, we can use the.sync modifier to abbreviate as follows:

<ChildComponent :title.sync="pageTitle" />

The sync is actually the grammatical sugar above, and you can see that it's very similar to the v-model usage

3.x Syntax

In 3.x, a v-model on a custom component is equivalent to passing a modelValue prop and receiving a thrown update:modelValue event:

<ChildComponent v-model="pageTitle" />

<!-- Is the following abbreviation: -->

<ChildComponent
  :modelValue="pageTitle"
  @update:modelValue="pageTitle = $event"
/>

If you need to change the name of the model, now we can pass a parameter to the v-model as an alternative to the model option within the component:

<ChildComponent v-model:title="pageTitle" />

<!-- Is the following abbreviation: -->

<ChildComponent :title="pageTitle" @update:title="pageTitle = $event" />


This can also be an alternative to the.sync modifier and allows us to use multiple v-model s on custom components.

<ChildComponent v-model:title="pageTitle" v-model:content="pageContent" />

<!-- Is the following abbreviation: -->

<ChildComponent
  :title="pageTitle"
  @update:title="pageTitle = $event"
  :content="pageContent"
  @update:content="pageContent = $event"
/>

Off-topic topic

How do you achieve similar functionality in React? By wrapping code that modifies the state in a function and passing it as prop to a child component, the reference to the closure changes after the child component is called, causing the parent component state to be modified, thereby changing the prop value passed in to the child component:

const Parent = () => {
	const [count, setCount] = React.useState(0);
	const plusOne = () => count++;
	return (
		<Child count={count} onClick={plusOne} />
	)
}

const Child = ({ count, onClick }) => {
	return (
		<>
			<span>{count}</span>
			<button onClick={onClick}>Click on me</button>
		</>
	)
}

Reference resources

Vue3 Official Document - v-model

Tags: Javascript Front-end Vue.js

Posted on Mon, 06 Dec 2021 13:12:49 -0500 by presence