Как работают provide/inject?
В Vue provide и inject — это механизм, с помощью которого можно передавать зависимости от родительского компонента к его потомкам на любом уровне вложенности, минуя промежуточные компоненты. Это удобно для управления глобальными данными, сервисами или конфигурацией в компонентной иерархии без явной передачи через props.
Общая идея
-
provide — используется в родительском компоненте, чтобы предоставить значение (или значения).
-
inject — используется в потомке, чтобы получить доступ к предоставленным данным.
Этот механизм работает аналогично Dependency Injection (внедрение зависимостей).
Когда использовать
-
Когда нужно передать данные глубоко вниз по иерархии, но не хочется пробрасывать props через несколько уровней.
-
Для сервисов (например, логгер, локализация, хранилище состояния, глобальные настройки).
-
При создании библиотек компонентов, чтобы внутренняя реализация могла взаимодействовать с контекстом.
Использование в Options API
Родительский компонент:
export default {
provide() {
return {
theme: 'dark',
user: { name: 'Анна' }
}
}
}
Потомок:
export default {
inject: \['theme', 'user'\],
mounted() {
console.log(this.theme) // 'dark'
console.log(this.user.name) // 'Анна'
}
}
Использование в Composition API
Родитель:
import { provide } from 'vue'
setup() {
const config = { locale: 'ru', darkMode: true }
provide('config', config)
}
Потомок:
import { inject } from 'vue'
setup() {
const config = inject('config')
return { config }
}
Значения реактивности
По умолчанию provide/inject не делает значения реактивными, если вы передаёте обычные объекты или примитивы.
Чтобы сделать передаваемое значение реактивным, используйте ref() или reactive():
Родитель:
import { provide, ref } from 'vue'
setup() {
const count = ref(0)
provide('count', count)
}
Потомок:
import { inject } from 'vue'
setup() {
const count = inject('count')
return { count } // count.value доступен и реактивен
}
Если передаётся обычное значение ('dark' или { name: 'Анна' }), то изменение родительского значения не обновит его в потомке. Нужно использовать ref или reactive для реактивности.
Указание значения по умолчанию
Если inject не находит переданного значения, можно указать значение по умолчанию:
const theme = inject('theme', 'light') // если не найдено — будет 'light'
Также можно использовать функцию по умолчанию:
const theme = inject('theme', () => 'light', true)
Параметр true означает, что значение по умолчанию будет результатом вызова функции, а не сама функция.
Передача нескольких значений
Можно предоставить несколько значений сразу, возвращая объект:
provide('appContext', {
locale: ref('ru'),
currency: 'USD'
})
И в потомке:
const context = inject('appContext')
console.log(context.locale.value) // 'ru'
Пример с контекстом темы
Родитель:
import { provide, ref } from 'vue'
setup() {
const theme = ref('light')
provide('theme', theme)
setTimeout(() => {
theme.value = 'dark'
}, 3000)
return {}
}
Дочерний:
import { inject } from 'vue'
setup() {
const theme = inject('theme')
return { theme }
}
<template>
<p>Текущая тема: {{ theme }}</p>
</template>
Через 3 секунды текст обновится, потому что theme реактивный (ref).
Особенности и ограничения
-
provide работает только вниз по иерархии. Потомок не может передать данные родителю.
-
inject работает только для потомков компонентов. Вы не можете использовать inject вне компонента.
-
Если потомок не находит значение — и значение по умолчанию не указано — результатом будет undefined.
Часто встречающиеся кейсы
1. Передача глобальной конфигурации
provide('config', {
lang: ref('en'),
currency: 'USD'
})
2. Внутреннее взаимодействие в библиотеке компонентов
Внутри библиотеки (например, UI-библиотека) можно использовать provide/inject для координации между родителем и дочерними частями (например, Tabs, TabItem).
Аналогия с React Context
Механизм похож на React Context API — родитель предоставляет значение, а потомки его читают. Отличие: в Vue это проще и легче использовать.
provide и inject — это мощный способ организации связи между компонентами, особенно когда они удалены друг от друга в иерархии. Это снижает связанность, упрощает поддержку и делает код чище в сложных интерфейсах.