Vue

<script setup>: defineProps, defineEmits, defineModel

Компонент карточки товара должен принимать название и цену снаружи, сообщать родителю о клике по кнопке покупки и поддерживать v-model для поля количества. В Options API это три разных секции: props, emits, и ручная связка для v-model. В script setup всё это три строки с макросами: defineProps, defineEmits, defineModel. Они выглядят как обычные функции, но это компиляторные макросы - их не нужно импортировать, и работают они только на этапе сборки. Понимание этой детали снимает путаницу, почему их нельзя вызвать как обычный код.

  • Карточка товара принимает title и price через defineProps и отдаёт событие add-to-cart через defineEmits
  • Кастомный компонент поля ввода поддерживает v-model родителя через defineModel без ручной связки prop и события
  • Модальное окно принимает флаг открытия через v-model и сообщает о закрытии событием наверх
  • Компонент-обёртка над сторонней библиотекой через defineExpose открывает родителю метод focus или reset
  • Типизированные props через дженерик defineProps дают автодополнение и проверку типов в IDE на этапе разработки

Предварительные знания

  • Понимание формата SFC и script setup из урока vue-03
  • Знакомство с ref из урока vue-06, потому что defineModel возвращает ref
  • Понимание односторонней передачи данных от родителя к ребёнку через props
  • Базовое знание событий и v-model из урока о шаблонах vue-04

defineProps и defineEmits

Компонент получает данные от родителя через props и сообщает родителю о событиях через emits. В script setup для этого есть два макроса. defineProps объявляет, какие входные данные компонент принимает, и возвращает объект для доступа к ним. defineEmits объявляет, какие события компонент может отправить, и возвращает функцию для их вызова. Это формализует контракт компонента: что входит и что выходит.

С TypeScript типы props и событий задаются дженериком, и это даёт проверку типов и автодополнение в IDE. Родитель передаёт props как атрибуты: title и price. При клике компонент вызывает emit с именем события add-to-cart, а родитель слушает его через @add-to-cart. Ключевое правило: props доступны только для чтения внутри ребёнка. Менять их напрямую нельзя - вместо этого ребёнок шлёт событие, и родитель сам решает, обновлять ли данные.

Односторонний поток данных вниз и события вверх делают поведение предсказуемым. Данные текут от родителя к детям через props, а изменения возвращаются наверх через события. Чтобы понять, откуда взялось значение, достаточно посмотреть на родителя, а не искать, кто из детей его поменял.

Почему ребёнок не должен менять полученный prop напрямую, а вместо этого шлёт событие наверх?

defineModel для v-model

Директива v-model на компоненте это двунаправленная привязка: родитель передаёт значение вниз и автоматически получает обновления наверх. Раньше для поддержки v-model в своём компоненте приходилось вручную объявлять prop modelValue и событие update modelValue, связывая их. Макрос defineModel убирает эту рутину: он создаёт двунаправленную привязку одной строкой и возвращает ref.

Возвращённый defineModel объект это ref. Чтение model даёт текущее значение, запись через model.value (или прямое присваивание в шаблоне) уведомляет родителя об изменении. Внутри компонента это выглядит как обычный локальный ref, но под капотом он синхронизирован с v-model родителя. В шаблоне выше при вводе текста присваивание в model сразу отражается в переменной name родителя.

Один компонент может иметь несколько v-model через именованные модели: defineModel('firstName') и defineModel('lastName'). Родитель привязывает их как v-model:first-name и v-model:last-name. Это удобно для компонентов вроде формы адреса, где наружу выходит несколько связанных значений.

Что возвращает макрос defineModel и как с ним работать?

Макросы компилятора и defineExpose

Важная деталь обо всех этих функциях: defineProps, defineEmits, defineModel и defineExpose это не обычные функции, а компиляторные макросы. Их не нужно импортировать из vue, и они существуют только внутри script setup. Компилятор Vue распознаёт их на этапе сборки и превращает в соответствующий код. Поэтому их нельзя вызвать условно, в цикле или вынести в отдельную функцию - они обрабатываются статически, а не выполняются в рантайме.

По умолчанию компонент на script setup закрыт снаружи: родитель, получивший ссылку на экземпляр компонента, не видит его внутренних переменных и методов. Это сознательная инкапсуляция. Макрос defineExpose открывает наружу только то, что явно перечислено. Через него родитель получает доступ к конкретному методу или значению ребёнка, например, чтобы вызвать focus у поля ввода или reset у формы.

defineExpose стоит применять экономно. Чем больше внутренностей компонента открыто наружу, тем сильнее родитель завязан на его реализацию, и тем труднее менять компонент без поломки. Предпочтительный способ общения это props вниз и события вверх. defineExpose оправдан для императивных действий вроде focus или reset, которые неудобно выразить через декларативный поток данных.

Почему defineProps и другие define-функции называют компиляторными макросами, а не обычными функциями?

Связь с другими темами

Этот урок собирает базовый инструментарий общения компонентов:

  • Single File Component — Макросы живут внутри блока script setup, формат которого разобран ранее
  • ref и reactive — defineModel возвращает ref, и работа с ним опирается на понимание реактивных контейнеров

Итог

  • defineProps, defineEmits, defineModel и defineExpose это компиляторные макросы: они не импортируются и обрабатываются на этапе сборки, а не выполняются как обычные функции в рантайме
  • defineProps объявляет типизированные входные данные компонента, которые родитель передаёт через атрибуты, props доступны только для чтения внутри ребёнка
  • defineEmits объявляет события, которые компонент шлёт наверх родителю, заменяя прямую мутацию props на явное уведомление
  • defineModel создаёт двунаправленную привязку для v-model, возвращая ref, чтение и запись которого синхронизированы с родителем без ручной пары prop и событие
  • defineExpose контролирует публичный интерфейс: по умолчанию script setup закрыт снаружи, и только перечисленное в defineExpose доступно родителю через ссылку на компонент

Связанные уроки

  • vue-03-sfc-components — Макросы работают только внутри script setup, формат которого разобран в третьем уроке
  • vue-06-ref-reactive — defineModel возвращает ref, а props читаются реактивно, поэтому понимание ref здесь применяется напрямую
<script setup>: defineProps, defineEmits, defineModel

0

1

Войти