Как объявляется состояние компонента: props, локальные данные и emits?
JuniorVue.js #vue #state #data
Вопрос
Какие есть способы объявить и передать состояние компонента — props, локальные данные, emits — и как они соотносятся друг с другом?
Короткий ответ
Props — это данные, которые родитель передаёт дочернему компоненту сверху вниз; они только для чтения внутри дочернего компонента. Локальное состояние объявляется через ref/reactive внутри <script setup> и принадлежит самому компоненту. Emits — это механизм, которым дочерний компонент сообщает родителю о событиях (снизу вверх), не изменяя его данные напрямую.
Подробный ответ
Vue-компонент — это изолированный кусок UI с собственными входами и выходами:
- Props (входные данные). Объявляются через
defineProps(). Поток данных — однонаправленный: родитель владеет значением и передаёт его вниз через атрибут (:count="value"). Дочерний компонент не должен мутировать проп напрямую — это считается антипаттерном и Vue выдаст предупреждение в dev-режиме. Если пропу нужно локальное изменяемое отражение, для этого заводят отдельныйref, инициализированный значением пропа (или используютdefineModel()для двусторонней привязки). - Локальное состояние. Данные, которые нужны только внутри компонента и не передаются извне, объявляются через
ref()(для примитивов и вообще любых значений) илиreactive()(для объектов). Это состояние живёт и умирает вместе с экземпляром компонента. - Emits (исходящие события). Поскольку проп менять напрямую нельзя, для связи «снизу вверх» дочерний компонент эмитит событие через
defineEmits(), а родитель подписывается на него через@event-nameи уже сам обновляет своё состояние. Это делает поток данных предсказуемым: всегда понятно, откуда пришло изменение.
Такая модель называется «props down, events up» — данные текут вниз по дереву компонентов, а изменения поднимаются наверх через события, вместо прямой мутации чужого состояния.
Пример
<!-- CounterButton.vue (дочерний компонент) -->
<script setup>
const props = defineProps({
modelValue: { type: Number, required: true }
})
const emit = defineEmits(['update:modelValue'])
function increment() {
// проп не мутируется напрямую — эмитим событие с новым значением
emit('update:modelValue', props.modelValue + 1)
}
</script>
<template>
<button @click="increment">+1 (сейчас: {{ modelValue }})</button>
</template>
<!-- Parent.vue -->
<script setup>
import { ref } from 'vue'
import CounterButton from './CounterButton.vue'
// локальное состояние родителя
const count = ref(0)
</script>
<template>
<!-- v-model — сахар над :modelValue + @update:modelValue -->
<CounterButton v-model="count" />
<p>Значение в родителе: {{ count }}</p>
</template>
Дополнительные вопросы
- Почему нельзя напрямую мутировать проп внутри дочернего компонента и что произойдёт, если попытаться?
- Чем
refотличается отreactiveпри объявлении локального состояния? - Как передать несколько именованных значений через
v-model(v-model:title,v-model:count)?