# Provide / inject
Подразумевается, что уже изучили и разобрались с разделом Основы компонентов. Если нет — прочитайте его сначала.
Обычно данные из родительского компонента в дочерний передаются с помощью входных параметров. Но представьте структуру, где есть несколько вложенных компонентов и требуется передать что-то из родительского компонента в глубоко вложенный дочерний. Придётся передавать входные параметры вниз по всей цепочке компонентов, что иногда может быть очень неудобно.
В таких случаях можно использовать пару provide
и inject
. Родительские компоненты могут служить провайдерами зависимостей для всех своих потомков, независимо от того, насколько глубокая иерархия компонентов. Работа этой возможности состоит из двух частей: родительский компонент имеет опцию provide
для предоставления данных, а дочерний компонент имеет опцию inject
для внедрения и использования этих данных.
Например, в такой иерархии компонентов:
Root
└─ TodoList
├─ TodoItem
└─ TodoListFooter
├─ ClearTodosButton
└─ TodoListStatistics
2
3
4
5
6
Если потребуется передать длину массива элементов todo-списка в TodoListStatistics
, то нужно будет передать входной параметр вниз по иерархии: TodoList
-> TodoListFooter
-> TodoListStatistics
. С использованием provide/inject это можно передать напрямую:
const app = Vue.createApp({})
app.component('todo-list', {
data() {
return {
todos: ['Покормить кота', 'Купить билеты']
}
},
provide: {
user: 'John Doe'
},
template: `
<div>
{{ todos.length }}
<!-- остальная часть шаблона компонента -->
</div>
`
})
app.component('todo-list-statistics', {
inject: ['user'],
created() {
console.log(`Внедрённое свойство: ${this.user}`) // > Внедрённое свойство: John Doe
}
})
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
Но это не сработает если указать здесь какое-нибудь свойство экземпляра компонента:
app.component('todo-list', {
data() {
return {
todos: ['Покормить кота', 'Купить билеты']
}
},
provide: {
todoLength: this.todos.length // приведёт к ошибке `Cannot read property 'length' of undefined`
},
template: `
...
`
})
2
3
4
5
6
7
8
9
10
11
12
13
Для доступа к свойствам экземпляра компонента необходимо сделать provide
функцией, которая возвращает объект:
app.component('todo-list', {
data() {
return {
todos: ['Покормить кота', 'Купить билеты']
}
},
provide() {
return {
todoLength: this.todos.length
}
},
template: `
...
`
})
2
3
4
5
6
7
8
9
10
11
12
13
14
15
Это позволит безопаснее изменять компонент, не опасаясь изменить/удалить что-нибудь, на что полагается дочерний компонент. Интерфейс между такими компонентами остаётся чётко определённым, как и в случае с входными параметрами.
В принципе, инъекцию зависимостей можно считать разновидностью «входных параметров дальнего действия», за исключением того что:
- родительские компоненты не должны знать, какие потомки используют свойства, которые они предоставляют
- дочерние компоненты не должны знать от кого внедряемые свойства приходят
# Работа с реактивностью
В примере выше изменения списка todos
не будут отражаться на внедряемом свойстве todoLength
. Потому что по умолчанию привязки provide/inject
не реактивны. Возможно изменить это поведение, передав в provide
ref
-свойство или объект reactive
. В этом случае, если потребуется реагировать на изменения в компоненте предке, необходимо присвоить computed
свойство из Composition API во внедряемое todoLength
:
app.component('todo-list', {
// ...
provide() {
return {
todoLength: Vue.computed(() => this.todos.length)
}
}
})
app.component('todo-list-statistics', {
inject: ['todoLength'],
created() {
console.log(`Внедряемое свойство: ${this.todoLength.value}`) // > Внедряемое свойство: 5
}
})
2
3
4
5
6
7
8
9
10
11
12
13
14
15
Теперь любое изменение todos.length
будет корректно отражаться в компонентах, где внедряется todoLength
. Подробнее про computed
в разделе Вычисляемые свойства и методы-наблюдатели, а про reactive
provide/inject в разделе Composition API.