Vue 3.4 Новая механика v-model компонента

Spread the love

Краткий перевод: https://vuejs.org/guide/components/v-model.html

Основное использование

v-model используется для реализации двусторонней привязки в компоненте.

Начиная с Vue 3.4, рекомендуемый подход для достижения этой цели — использование макроса defineModel():

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

function update() {
  model.value++
}
</script>

<template>
  <div>parent bound v-model is: {{ model }}</div>
</template>

Затем родительский компонент может связать значение с v-model:

<!-- Parent.vue -->
<Child v-model="count" />

Значение, возвращаемое методом defineModel() , является реактивной переменной ref. К нему можно получить доступ и изменить его, как и к любой другой ref, за исключением того, что он действует как двусторонняя привязка между родительским значением и локальным значением:

  • Его .value синхронизируется со значением, связанным родительской v-model;
  • Когда .value изменяется внутри дочернего элемента, это также приводит к обновлению значения внутри родительского элемента.

Это означает, что вы также можете привязать этот ref нативному элементу ввода (input) с помощью v-model, что упрощает расположение элементов ввода input (снаружи или внутри родительского компонента), обеспечивая при этом такое же использование v-model:

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

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

Playground Example

Под капотом

defineModel это удобный макрос. Компилятор расширяет его до следующего::

  • Создает prop с именем modelValue, с которой синхронизируется локальное значение ref;
  • Создает событие с именем update:modelValue, которое генерируется при изменении значения локальной ref.

Вот как вы могли бы реализовать тот же дочерний компонент, показанный выше до версии 3.4:

<script setup>
const props = defineProps(['modelValue'])
const emit = defineEmits(['update:modelValue'])
</script>

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

Как видите, это немного более многословно. Однако полезно знать, что происходит под капотом.

Так же поскольку defineModel объявляет prop, вы можете указать параметры свойства prop, передав их в defineModel:

// making the v-model required
const model = defineModel({ required: true })

// providing a default value
const model = defineModel({ default: 0 })а

Аргументы v-model

v-model также может принимать аргументы:

<MyComponent v-model:title="bookTitle" />

Мы можем передать в дочерний компонент соответствующий аргумент, указав строку в defineModel() в качестве первого аргумента:

<!-- MyComponent.vue -->
<script setup>
const title = defineModel('title')
</script>

<template>
  <input type="text" v-model="title" />
</template>

Try it in the Playground

Если также необходимы параметры prop, их следует указывать после имени модели:

const title = defineModel('title', { required: true })

Множественное использование v-model 

Ранее мы рассмотрели использование одного prop и события, с помощью аргументов v-model, но вы так же можете создавать несколько привязок v-model для одного экземпляра компонента.

Каждая v-model будет синхронизироваться со своим prop без необходимости использования дополнительных параметров в компоненте:

<UserName
  v-model:first-name="first"
  v-model:last-name="last"
/>
<script setup>
const firstName = defineModel('firstName')
const lastName = defineModel('lastName')
</script>

<template>
  <input type="text" v-model="firstName" />
  <input type="text" v-model="lastName" />
</template>

Try it in the Playground

Использование модификаторов v-model 

В некоторых случаях вам также может потребоваться, чтобы v-model вашего компонента ввода (input) поддерживала стандартные пользовательские модификаторы (например такие как .trim.number .lazy).

Пример использования модификатора capitalize, который делать первую букву строки всегда заглавной в родительском компоненте:

<MyComponent v-model.capitalize="myText" />

Доступ к модификаторам, добавленным к v-model компонента, можно получить в дочернем компоненте, деструктурировав возвращаемое значение defineModel() следующим образом:

<script setup>
const [model, modifiers] = defineModel()

console.log(modifiers) // { capitalize: true }
</script>

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

Чтобы условно настроить способ чтения/записи значения на основе модификаторов, мы можем передать параметры get и set в defineModel(). Эти два параметра получают значение при получении/установке ref на модель и должны возвращать преобразованное значение. Вот как мы можем использовать опцию set для реализации модификатора capitalize:

<script setup>
const [model, modifiers] = defineModel({
  set(value) {
    if (modifiers.capitalize) {
      return value.charAt(0).toUpperCase() + value.slice(1)
    }
    return value
  }
})
</script>

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

Try it in the Playground

Модификаторы для v-model с аргументами

Вот еще один пример использования модификаторов с несколькими v-model с разными аргументами:

<UserName
  v-model:first-name.capitalize="first"
  v-model:last-name.uppercase="last"
/>
<script setup>
const [firstName, firstNameModifiers] = defineModel('firstName')
const [lastName, lastNameModifiers] = defineModel('lastName')

console.log(firstNameModifiers) // { capitalize: true }
console.log(lastNameModifiers) // { uppercase: true}
</script>

Была ли вам полезна эта статья?
[9 / 4.9]

Spread the love
Подписаться
Уведомление о
guest
1 Комментарий
Oldest
Newest Most Voted
Inline Feedbacks
View all comments
Анонимно
Анонимно
2 дней назад

Супер