Олег Марков
Передача и использование props в Vue 3 для взаимодействия компонентов
Введение
В приложениях на Vue 3 компоненты часто “разговаривают” между собой, обмениваясь данными для построения динамичных пользовательских интерфейсов. Одним из главных инструментов такого взаимодействия выступают props — специальные свойства, позволяющие родительскому компоненту передавать данные своим потомкам. Правильное использование props делает архитектуру приложения более модульной, предсказуемой и удобной для поддержки.
Смотрите, я покажу вам, как работает эта система: родительский компонент отправляет значения в дочерний, а тот — использует их для отображения или логики. Мы рассмотрим, как объявлять props, задавать им типы, передавать значения различного типа, применять валидацию и отслеживать изменения. Давайте вместе посмотрим на примерах, как всё это реализовано на практике.
Как работают props в Vue 3
Основная концепция передачи данных
Props (от “properties”) — это способ передать значения из родительского компонента в дочерний. Ключевой идеей props является однонаправленный поток данных (“top-down”), то есть данные идут только от родителя к наследнику, а не наоборот. Тем самым props помогают избегать непредсказуемого изменения данных и делают поведение приложения прозрачным.
Пример передачи props
Допустим, у вас есть два компонента: ParentComponent
и ChildComponent
. Родитель передаёт сообщение в дочерний компонент:
<!-- ParentComponent.vue -->
<template>
<ChildComponent message="Привет, Vue 3!" />
</template>
<script setup>
import ChildComponent from './ChildComponent.vue'
</script>
<!-- ChildComponent.vue -->
<template>
<p>{{ message }}</p>
</template>
<script setup>
// Здесь мы явно объявляем пропс 'message'
defineProps(['message'])
</script>
Как видите, родитель передаёт значение через атрибут, а дочерний его принимает и отображает.
Объявление, типизация и валидация props
В Vue 3, особенно при использовании <script setup>
, объявление props осуществляется с помощью функции defineProps
.
Объявление props как массива строк
// Принимаем только перечисленные пропсы (в данном случае, message)
const props = defineProps(['message'])
Часто достаточно просто объявить список props, которые вы собираетесь использовать.
Типизация и объектная форма объявления
Более гибко — использовать объект. Давайте посмотрим, как добавить тип и значения по умолчанию:
const props = defineProps({
message: String,
count: {
type: Number,
required: true, // Обязательный пропс
default: 0 // Значение по умолчанию
}
})
Здесь message
обязательно должен быть строкой, а count
— числом с дефолтным значением 0
. Если родитель не передаст count
, компонент сам подставит это значение.
Валидация props
Можно проверять значения и с помощью validator:
const props = defineProps({
status: {
type: String,
validator: (value) => ['success', 'error', 'pending'].includes(value)
}
})
// Теперь статус может быть только одним из трёх указанных вариантов
Если переданное значение не проходит проверку — консоль покажет предупреждение, что полезно при отладке.
Использование props в шаблоне и скрипте
Любое значение, объявленное как prop, становится реактивным и доступно в шаблоне, скрипте и вычисляемых свойствах. Например:
<template>
<h2>Счетчик: {{ count }}</h2>
</template>
<script setup>
const props = defineProps({
count: Number
})
// Вы также можете использовать props.count внутри функций
</script>
Передача данных различных типов
Props могут быть любыми: числа, строки, объекты, массивы, функции.
Передача объектов и массивов
<!-- ParentComponent.vue -->
<ChildComponent :user="{ name: 'Елена', age: 32 }" :tasks="['Покупки', 'Учёба']" />
// ChildComponent.vue
const props = defineProps({
user: Object,
tasks: Array
})
// Теперь props.user — это объект, а props.tasks — массив
Передача функций как props
Давайте посмотрим на передачу обработчика событий:
<!-- ParentComponent.vue -->
<ChildComponent :onSubmit="handleSubmit" />
// ChildComponent.vue
const props = defineProps({
onSubmit: Function
})
// Где-то внутри компонента можно вызвать: props.onSubmit()
Это удобно, например, для коллбэков, когда нужно, чтобы дочерний компонент “сообщил” о каком-то действии родителю.
Reactivity: что можно и нельзя делать с prop
Props в дочернем компоненте реактивны — если родитель обновляет значение, дочерний компонент увидит новое значение.
Однако Vue запрещает напрямую изменять переданный prop внутри дочернего компонента — это вызовет предупреждение. Смотрите, как это проявляется:
// Такое делать нельзя!
props.count++ // Вы получите ошибку в консоли
Чтобы обойти это (например, если вы хотите временно изменить значение), создайте локальное состояние:
import {ref, watch} from 'vue'
const props = defineProps({ count: Number })
const localCount = ref(props.count)
watch(() => props.count, (newVal) => {
localCount.value = newVal
})
// Теперь изменяйте localCount, а обновления от родителя попадут сюда через watch
Значения по умолчанию при отсутствии пропса
Если пропс не передан родителем, можно определить значение по умолчанию:
const props = defineProps({
username: {
type: String,
default: 'Гость'
}
})
Теперь, если username
не указан при использовании компонента, он будет 'Гость'.
Обязательные (required) props
Добавьте required: true
, чтобы получать предупреждение, если родитель не передал важный prop:
const props = defineProps({
id: {
type: [Number, String],
required: true
}
})
Передача всех props через v-bind
Если у вас есть объект со множеством свойств, удобно передавать его целиком так:
<ChildComponent v-bind="userProps" />
<!-- Все поля userProps попадут в дочерний компонент как отдельные пропсы -->
В скрипте родителя:
const userProps = {
name: 'Олег',
age: 40,
status: 'active'
}
Дочерний компонент определяет ожидаемые props:
const props = defineProps({
name: String,
age: Number,
status: String
})
Примеры типичных паттернов передачи props
Алиас пропсов в дочерних компонентах
Иногда хочется переименовать пропс, чтобы избежать конфликтов. Используйте композитные свойства:
const props = defineProps({ message: String })
const localMessage = computed(() => props.message)
“Дочерний компонент как шаблонное API” (Scoped Slots)
В Vue 3 slots и props тесно связаны. Родитель может выступать поставщиком UI, а дочерний компонент — передавать данные через scoped slots:
<ChildComponent v-slot="{ status }">
<span>Статус: {{ status }}</span>
</ChildComponent>
Внутри дочернего компонента определяйте данные для вывода через slot:
<template #default="{ status }">
<slot :status="currentStatus"></slot>
</template>
Мутация объектов и массивов, переданных через props
Если вы передаете объект или массив как prop, сам указатель на объект менять нельзя, но внутренние поля — можно (хотя это не рекомендуется). Это потому, что переданный объект — это ссылка на оригинал.
// Такое изменение внутреннего свойства будет работать:
props.user.name = 'Другое Имя'
// Но это не лучшая практика, старайтесь избегать мутаций prop
Более надёжный вариант — клонировать объект в локальное состояние.
Жизненный цикл props: отслеживание изменений
Вы можете отслеживать изменение любого пропса с помощью watch:
import {watch} from 'vue'
const props = defineProps({ value: Number })
watch(() => props.value, (newVal, oldVal) => {
// Здесь срабатывает реакция на изменение prop
})
Это полезно, когда на изменение пропса нужно отреагировать выполнением действия.
Reactivity мегаподробнее
- Числовые, строковые, булевые пропсы — обновляются мгновенно при изменении родителем.
- Объекты и массивы — реактивны по ссылке (если внутри поля объекта меняются, дочерний это увидит).
- Простое “переприсваивание” пропса в дочернем не приведет к обновлению родителя.
Передача props во вложенные компоненты
Если у вас есть многоуровневая иерархия компонентов, передавать prop “вниз” приходится вручную:
<!-- App.vue -->
<ParentComponent user="..."/>
<!-- ParentComponent.vue -->
<ChildComponent :user="user"/>
<!-- ChildComponent.vue -->
// Вот здесь проп user доходит до самой глубины
const props = defineProps({ user: Object })
Для сложных случаев советую обратить внимание на Provide/Inject или глобальные state management решения.
Ограничения: что не стоит делать с props
- Не изменяйте prop напрямую внутри дочернего компонента.
- Не используйте props для передачи данных “вверх” (от дочернего к родителю) — для этого применяют события (emit).
- Не полагайтесь на мутацию объектов через prop — это может привести к багам.
Лучшие практики
- Явно объявляйте все используемые props.
- Типизируйте props для лучшей читаемости и отладки.
- Используйте значения по умолчанию и валидацию.
- По возможности делайте prop неизменяемым внутри дочернего компонента.
- Minimize props (передавайте только нужные данные).
Заключение
Props — это фундаментальная механика, на которой строится взаимодействие компонентов в Vue 3. Они обеспечивают односторонний поток данных от родителя к потомку, дают большие возможности по типизации и валидации и делают логику передачи данных прозрачной и управляемой. Изучив основы работы с props, вы сможете строить масштабируемые, модульные и надёжные Vue-приложения.
Частозадаваемые технические вопросы по теме статьи и ответы на них
Как заставить дочерний компонент отправить измененное значение пропса обратно родителю?
Вам нужно использовать механизм событий (emit
) вместо прямого изменения пропса. Например:
const emit = defineEmits(['update'])
function updateValue(newValue) {
emit('update', newValue)
}
Родитель слушает событие:<ChildComponent @update="handleUpdate" />
Как динамически изменить список props, принимаемых компонентом?
Вариативно “переключать” props на лету нельзя. Компонент принимает только те props, которые объявлены (или все неограниченно, если не указан defineProps). Чтобы пропс был реактивным, просто обновляйте значение у родителя — оно мгновенно попадет в дочерний компонент.
Как сделать prop обязательным только при определённых условиях?
Добавьте computed валидацию в родителе и условно передавайте prop только при удовлетворении условий. Локальная валидация через validator не поддерживает динамическое required.
Можно ли передавать JSX/шаблоны как prop?
Для этого используют scoped slots или функцию render-prop, обычный prop не рассчитан на передачу готового шаблона.
Как запретить передачу “лишних” props?
Vue по умолчанию пропустит лишние props, не объявленные в defineProps, только если компонент объявлен с <script setup>
. Для обычных компонентов используйте inheritAttrs: false
и явно устанавливайте только необходимые props в шаблон.