# 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: ['Feed a cat', 'Buy tickets']
}
},
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: ['Feed a cat', 'Buy tickets']
}
},
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: ['Feed a cat', 'Buy tickets']
}
},
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
не реактивны по умолчанию. Можно изменить это поведение, передав свойство ref
или объект reactive
в provide
. В данном случае, если требуется реагировать на изменения в компоненте предке, необходимо присвоить свойство 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
можно изучить в разделе Computed и Watch, а про reactive
provide/inject в разделе Composition API.