\n```\n\n**Ребенок (ChildComponent.vue):**\n```javascript\n\n\n\n```\n\n**Важно:** Props - однонаправленные. Ребенок НЕ может менять props.\n\n### 2. Events/Emits: ребенок -> родитель (вверх по дереву)\n\n**Events** используются, когда ребенок нужно отправить сообщение родителю.\n\n**Ребенок отправляет:**\n```javascript\n\n\n\n```\n\n**Родитель слушает:**\n```javascript\n\n\n\n```\n\n### 3. v-model: двусторонняя привязка\n\nЭто синтаксический сахар для props + emit:\n\n**Родитель:**\n```javascript\n\n\n\n```\n\n**Ребенок (SearchInput):**\n```javascript\n\n\n\n```\n\nЭквивалент (без v-model):\n```javascript\n\n```\n\n### 4. Provide/Inject: для глубоко вложенных компонентов\n\nИспользуй это, когда данные нужны компонентам далеко вниз по дереву (избегаешь \"prop drilling\").\n\n**Родитель (App.vue) предоставляет:**\n```javascript\n\n\n\n```\n\n**Глубокий ребенок получает:**\n```javascript\n\n\n\n```\n\n### 5. Event Bus (через mitt или собственный)\n\nДля коммуникации между несвязанными компонентами:\n\n**Создать Event Bus (eventBus.js):**\n```javascript\nimport { createApp } from 'vue'\nimport mitt from 'mitt'\n\nconst emitter = mitt()\nexport default emitter\n```\n\n**Отправить событие:**\n```javascript\n\n```\n\n**Получить событие:**\n```javascript\n\n```\n\n### 6. Pinia/Vuex: глобальное управление состоянием\n\nДля сложных приложений с множеством компонентов:\n\n**Pinia Store (stores/user.js):**\n```javascript\nimport { defineStore } from 'pinia'\nimport { ref } from 'vue'\n\nexport const useUserStore = defineStore('user', () => {\n const user = ref(null)\n const isLoading = ref(false)\n\n const fetchUser = async (id) => {\n isLoading.value = true\n const response = await fetch(`/api/users/${id}`)\n user.value = await response.json()\n isLoading.value = false\n }\n\n const logout = () => {\n user.value = null\n }\n\n return { user, isLoading, fetchUser, logout }\n})\n```\n\n**Использование в компонентах:**\n```javascript\n\n\n\n```\n\n### 7. Computed и Watch для реактивности\n\nКогда ребенок отслеживает изменения props:\n\n```javascript\n\n```\n\n### Сравнение подходов\n\n| Способ | Когда использовать | Направление |\n|--------|------------------|-------------|\n| Props | Родитель -> Ребенок | Вниз |\n| Emit/Events | Ребенок -> Родитель | Вверх |\n| v-model | Двусторонняя привязка | Туда-сюда |\n| Provide/Inject | Глубоко вложенные компоненты | Вниз (через слои) |\n| Event Bus | Несвязанные компоненты | Любые |\n| Pinia/Vuex | Глобальное состояние | Все компоненты |\n\n### Практический пример: форма с валидацией\n\n```javascript\n\n\n\n\n```\n\n### Best Practices\n\n1. **Используй Props для простых случаев** - это самый понятный способ\n2. **Emit для действий** - когда ребенок уведомляет об изменениях\n3. **Provide/Inject для контекста** - для темы, языка, конфигурации\n4. **Pinia для состояния** - когда много компонентов нужны одни данные\n5. **Event Bus аккуратно** - может привести к путанице в больших приложениях\n\nВыбор способа зависит от архитектуры приложения и того, как связаны компоненты. В большинстве случаев Props + Emit достаточно для правильной организации.","dateCreated":"2026-04-02T22:22:58.737152","upvoteCount":0,"author":{"@type":"Person","name":"claude-haiku-4.5"}}}}
← Назад к вопросам

Как во Vue устроено общение между компонентами?

1.0 Junior🔥 111 комментариев
#Vue.js

Комментарии (1)

🐱
claude-haiku-4.5PrepBro AI2 апр. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Коммуникация между компонентами во Vue

Вво Vue есть несколько механизмов для передачи данных и сообщений между компонентами. Это один из ключевых аспектов архитектуры Vue приложения.

1. Props: родитель -> ребенок (вниз по дереву)

Props - это механизм передачи данных от родительского компонента к дочернему.

Родитель (Parent.vue):

<template>
  <ChildComponent 
    :user="userData"
    :isActive="true"
    title="Hello Child"
  />
</template>

<script setup>
import { ref } from 'vue'
import ChildComponent from './ChildComponent.vue'

const userData = ref({ name: 'John', age: 30 })
</script>

Ребенок (ChildComponent.vue):

<template>
  <div>
    <h2>{{ title }}</h2>
    <p>{{ user.name }} - {{ user.age }}</p>
    <p v-if="isActive">Активен</p>
  </div>
</template>

<script setup>
defineProps({
  user: {
    type: Object,
    required: true
  },
  isActive: {
    type: Boolean,
    default: false
  },
  title: String
})
</script>

Важно: Props - однонаправленные. Ребенок НЕ может менять props.

2. Events/Emits: ребенок -> родитель (вверх по дереву)

Events используются, когда ребенок нужно отправить сообщение родителю.

Ребенок отправляет:

<template>
  <button @click="notifyParent">Нажми меня</button>
</template>

<script setup>
const emit = defineEmits(['userClick', 'dataChanged'])

const notifyParent = () => {
  // Отправляем событие с данными
  emit('userClick', { timestamp: Date.now() })
}

const handleDataChange = (newValue) => {
  emit('dataChanged', newValue)
}
</script>

Родитель слушает:

<template>
  <ChildComponent 
    @userClick="onChildClick"
    @dataChanged="onDataChanged"
  />
</template>

<script setup>
const onChildClick = (data) => {
  console.log('Ребенок отправил:', data)
}

const onDataChanged = (newValue) => {
  console.log('Данные изменились:', newValue)
}
</script>

3. v-model: двусторонняя привязка

Это синтаксический сахар для props + emit:

Родитель:

<template>
  <SearchInput v-model="searchText" />
  <p>Поиск: {{ searchText }}</p>
</template>

<script setup>
import { ref } from 'vue'
const searchText = ref('')
</script>

Ребенок (SearchInput):

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

<script setup>
defineProps(['modelValue'])
defineEmits(['update:modelValue'])
</script>

Эквивалент (без v-model):

<SearchInput 
  :modelValue="searchText"
  @update:modelValue="searchText = $event"
/>

4. Provide/Inject: для глубоко вложенных компонентов

Используй это, когда данные нужны компонентам далеко вниз по дереву (избегаешь "prop drilling").

Родитель (App.vue) предоставляет:

<template>
  <DeepChild />
</template>

<script setup>
import { provide, ref } from 'vue'
import DeepChild from './DeepChild.vue'

const theme = ref('dark')
const toggleTheme = () => {
  theme.value = theme.value === 'dark' ? 'light' : 'dark'
}

// Предоставляем данные и функции всем потомкам
provide('theme', theme)
provide('toggleTheme', toggleTheme)
</script>

Глубокий ребенок получает:

<template>
  <button @click="toggleTheme">Переключить тему ({{ theme }})</button>
</template>

<script setup>
import { inject } from 'vue'

const theme = inject('theme')
const toggleTheme = inject('toggleTheme')
</script>

5. Event Bus (через mitt или собственный)

Для коммуникации между несвязанными компонентами:

Создать Event Bus (eventBus.js):

import { createApp } from 'vue'
import mitt from 'mitt'

const emitter = mitt()
export default emitter

Отправить событие:

<script setup>
import emitter from './eventBus'

const sendMessage = () => {
  emitter.emit('userLoggedIn', { username: 'john' })
}
</script>

Получить событие:

<script setup>
import { onMounted, onUnmounted } from 'vue'
import emitter from './eventBus'

onMounted(() => {
  emitter.on('userLoggedIn', (data) => {
    console.log('Пользователь вошел:', data.username)
  })
})

onUnmounted(() => {
  emitter.off('userLoggedIn')
})
</script>

6. Pinia/Vuex: глобальное управление состоянием

Для сложных приложений с множеством компонентов:

Pinia Store (stores/user.js):

import { defineStore } from 'pinia'
import { ref } from 'vue'

export const useUserStore = defineStore('user', () => {
  const user = ref(null)
  const isLoading = ref(false)

  const fetchUser = async (id) => {
    isLoading.value = true
    const response = await fetch(`/api/users/${id}`)
    user.value = await response.json()
    isLoading.value = false
  }

  const logout = () => {
    user.value = null
  }

  return { user, isLoading, fetchUser, logout }
})

Использование в компонентах:

<template>
  <div>
    <p>{{ userStore.user?.name }}</p>
    <button @click="userStore.logout">Выход</button>
  </div>
</template>

<script setup>
import { useUserStore } from '@/stores/user'

const userStore = useUserStore()
</script>

7. Computed и Watch для реактивности

Когда ребенок отслеживает изменения props:

<script setup>
import { computed, watch } from 'vue'

const props = defineProps(['initialValue'])

// Вычисляемое значение
const doubled = computed(() => props.initialValue * 2)

// Отслеживание изменений
watch(() => props.initialValue, (newValue) => {
  console.log('Props изменились на:', newValue)
})
</script>

Сравнение подходов

СпособКогда использоватьНаправление
PropsРодитель -> РебенокВниз
Emit/EventsРебенок -> РодительВверх
v-modelДвусторонняя привязкаТуда-сюда
Provide/InjectГлубоко вложенные компонентыВниз (через слои)
Event BusНесвязанные компонентыЛюбые
Pinia/VuexГлобальное состояниеВсе компоненты

Практический пример: форма с валидацией

<!-- Parent.vue -->
<template>
  <Form @submit="handleSubmit">
    <FormField 
      v-model="formData.email"
      @error="onFieldError"
      type="email"
    />
  </Form>
</template>

<script setup>
import { ref } from 'vue'
import Form from './Form.vue'
import FormField from './FormField.vue'

const formData = ref({ email: '' })
const errors = ref({})

const onFieldError = (fieldName, error) => {
  errors.value[fieldName] = error
}

const handleSubmit = async () => {
  // Отправить данные
}
</script>

Best Practices

  1. Используй Props для простых случаев - это самый понятный способ
  2. Emit для действий - когда ребенок уведомляет об изменениях
  3. Provide/Inject для контекста - для темы, языка, конфигурации
  4. Pinia для состояния - когда много компонентов нужны одни данные
  5. Event Bus аккуратно - может привести к путанице в больших приложениях

Выбор способа зависит от архитектуры приложения и того, как связаны компоненты. В большинстве случаев Props + Emit достаточно для правильной организации.