Provide / Inject
Эта страница предполагает, что вы уже прочитали Основы компонентов.
Сквозная передача пропсов
Обычно, когда нам нужно передать данные от родительского компонента к дочернему, мы используем пропсы. Однако представьте себе случай, когда у нас большое дерево компонентов, и глубоко вложенному компоненту нужно что-то от компонента дальнего предка. При использовании только пропсов нам пришлось бы передавать один и тот же проп по всей родительской цепочке:

Обратите внимание, что хотя компонент <Footer> может вообще не заботиться об этих пропсах, ему всё равно нужно объявить и передать их, чтобы <DeepChild> мог получить к ним доступ. Если родительская цепочка длиннее, то на пути к ней будет затронуто больше компонентов. Это называется сквозной передачей. и с ним определённо не очень весело иметь дело.
Мы можем решить проблему сквозной передачи пропсов с помощью provide и inject. Родительский компонент может служить провайдером зависимостей для всех своих потомков. Любой компонент в дереве-потомке, независимо от его глубины, может инжектировать зависимости, предоставленные компонентами, расположенными выше в его родительской цепочке.

Provide
Чтобы предоставить данные потомкам компонента, используйте функцию provide():
vue
<script setup>
import { provide } from 'vue'
provide(/* ключ */ 'message', /* значение */ 'hello!')
</script>Если вы не используете <script setup>, убедитесь, что provide() вызывается синхронно внутри setup():
js
import { provide } from 'vue'
export default {
setup() {
provide(/* ключ */ 'message', /* значение */ 'hello!')
}
}Функция provide() принимает два аргумента. Первый аргумент называется ключ инъекции, который может быть строкой или символом. Ключ инъекции используется компонентами-потомками для поиска нужного значения для инъекции. Один компонент может вызывать provide() несколько раз с разными ключами инъекции для предоставления различных значений.
Второй аргумент — это предоставленное значение. Значение может быть любого типа, включая реактивное состояние, такое как refs:
js
import { ref, provide } from 'vue'
const count = ref(0)
provide('key', count)Предоставление реактивных значений позволяет компонентам-потомкам, использующим предоставленное значение, устанавливать реактивное соединение с компонентом-провайдером.
Provide на уровне приложения
Помимо предоставления данных в компоненте, мы также можем предоставлять их на уровне приложения:
js
import { createApp } from 'vue'
const app = createApp({})
app.provide(/* ключ */ 'message', /* значение */ 'hello!')Предоставления на уровне приложения доступны для всех компонентов, отображаемых в приложении. Это особенно полезно при написании плагинов, так как плагины обычно не могут предоставлять значения с помощью компонентов.
Inject
Чтобы внедрить данные, предоставленные компонентом-предком, используйте функцию inject():
vue
<script setup>
import { inject } from 'vue'
const message = inject('message')
</script>Если несколько родительских компонентов предоставляют данные с одинаковым ключом, функция inject выберет значение от ближайшего родителя в цепочке родительских компонентов.
Если предоставленное значение является ссылкой, оно будет инжектировано как есть и не будет автоматически разворачиваться. Это позволяет компоненту-инжектору сохранять реактивную связь с компонентом-провайдером.
Полный пример provide + inject с реактивностью
Опять же, если не используется <script setup>, inject() следует вызывать синхронно только внутри setup():
js
import { inject } from 'vue'
export default {
setup() {
const message = inject('message')
return { message }
}
}Значения по умолчанию для инъекций
По умолчанию inject предполагает, что инжектируемый ключ предоставляется где-то в родительской цепочке. В случае, если ключ не указан, будет выдано предупреждение во время выполнения.
Если мы хотим, чтобы инжектируемое свойство работало с необязательными провайдерами, нам нужно объявить значение по умолчанию, аналогично props:
js
// `value` будет содержать "значение по умолчанию"
// если не было предоставлено данных, соответствующих «сообщению»
const value = inject('сообщение', 'значение по умолчанию')В некоторых случаях значение по умолчанию может потребоваться создать путём вызова функции или инстанцирования нового класса. Чтобы избежать лишних вычислений или побочных эффектов в случае, если необязательное значение не используется, мы можем использовать фабричную функцию для создания значения по умолчанию:
js
const value = inject('key', () => new ExpensiveClass(), true)Третий параметр указывает, что значение по умолчанию должно рассматриваться как фабричная функция.
Работа с реактивностью
При использовании реактивного предоставления / инъекции значений, рекомендуется держать любые мутации реактивного состояния внутри провайдера, когда это возможно. Это гарантирует, что предоставленное состояние и его возможные мутации будут находиться в одном компоненте, что упрощает их поддержку в будущем.
Бывают случаи, когда нам необходимо обновить данные компонента-инжектора. В таких случаях мы рекомендуем предоставлять функцию, которая отвечает за изменение состояния:
vue
<!-- внутри компонента провайдера -->
<script setup>
import { provide, ref } from 'vue'
const location = ref('North Pole')
function updateLocation() {
location.value = 'South Pole'
}
provide('location', {
location,
updateLocation
})
</script>vue
<!-- в компоненте инжектора -->
<script setup>
import { inject } from 'vue'
const { location, updateLocation } = inject('location')
</script>
<template>
<button @click="updateLocation">{{ location }}</button>
</template>Наконец, вы можете обернуть предоставленное значение с помощью readonly(), если хотите убедиться, что данные, переданные через provide, не могут быть изменены компонентом-инжектором.
vue
<script setup>
import { ref, provide, readonly } from 'vue'
const count = ref(0)
provide('read-only-count', readonly(count))
</script>Работа с символьными клавишами
До сих пор в примерах мы использовали ключи для инъекции строк. Если вы работаете в большом приложении с большим количеством поставщиков зависимостей или создаете компоненты, которые будут использоваться другими разработчиками, лучше всего использовать ключи инъекции Symbol, чтобы избежать возможных коллизий.
Рекомендуется экспортировать символы в отдельный файл:
js
export const myInjectionKey = Symbol()js
// в компоненте провайдера
import { provide } from 'vue'
import { myInjectionKey } from './keys.js'
provide(myInjectionKey, {
/* данные для предоставления */
})js
// в компоненте инжектора
import { inject } from 'vue'
import { myInjectionKey } from './keys.js'
const injected = inject(myInjectionKey)См. также: Типизация Provide / Inject