Передача данных вглубь: provide и inject
Иногда данные нужны компоненту, который лежит на несколько уровней ниже: App.vue → TodoList.vue → TodoItem.vue. Передавать их через props каждого промежуточного компонента утомительно. Функция provide делает значение доступным всем потомкам, а inject достаёт его там, где оно нужно.
Предоставить значение
В src/App.vue вызовите provide с ключом и значением:
<!-- src/App.vue -->
<script setup>
import { ref, provide } from 'vue'
import TodoList from './components/TodoList.vue'
const theme = ref('light')
provide('theme', theme)
</script>
<template>
<button @click="theme = theme === 'light' ? 'dark' : 'light'">
Сменить тему
</button>
<TodoList />
</template>Ключ 'theme' — имя, по которому потомки найдут значение. Передан сам ref, поэтому потомки увидят его изменения.
Получить значение
В любом вложенном компоненте, например src/components/TodoItem.vue, вызовите inject с тем же ключом:
<!-- src/components/TodoItem.vue -->
<script setup>
import { inject } from 'vue'
defineProps({ todo: Object })
const theme = inject('theme')
</script>
<template>
<li :class="theme">{{ todo.text }}</li>
</template>
<style scoped>
.dark {
background: #222;
color: #eee;
}
</style>Кнопка «Сменить тему» в App.vue переключает фон всех задач на тёмный и обратно. TodoList ничего не знает о теме и не передаёт её.
Значение по умолчанию
Если ни один предок не вызвал provide с этим ключом, inject вернёт undefined и Vue выведет предупреждение. Второй аргумент задаёт запасное значение:
<script setup>
import { inject } from 'vue'
const theme = inject('theme', 'light')
</script>Так компонент будет работать и вне App.vue, например в тесте.
Изменения только у владельца
Чтобы потомки не меняли общее значение как попало, вместе с ним передают функцию для изменения:
<!-- src/App.vue -->
<script setup>
import { ref, provide, readonly } from 'vue'
const theme = ref('light')
function toggleTheme() {
theme.value = theme.value === 'light' ? 'dark' : 'light'
}
provide('theme', { theme: readonly(theme), toggleTheme })
</script><!-- src/components/ThemeButton.vue -->
<script setup>
import { inject } from 'vue'
const { theme, toggleTheme } = inject('theme')
</script>
<template>
<button @click="toggleTheme">Тема: {{ theme }}</button>
</template>readonly возвращает версию ref, которую нельзя изменить. Менять тему можно только через toggleTheme — код изменения остаётся в одном месте.
Когда что использовать
| Ситуация | Способ |
|---|---|
| Данные нужны прямому потомку | props |
| Одно значение для целой ветки дерева: тема, язык | provide и inject |
| Общие данные всего приложения: пользователь, корзина | Хранилище Pinia (глава про Pinia) |
Частая ошибка
Передать в provide значение вместо ref: provide('theme', theme.value). Потомки получат строку 'light' на момент вызова и никогда не узнают о смене темы. Передавайте сам ref — так связь сохраняется.