# Provide / inject

Подразумевается, что уже изучили и разобрались с разделом Основы компонентов. Если нет — прочитайте его сначала.

Обычно данные из родительского компонента в дочерний передаются с помощью входных параметров. Но представьте структуру, где есть несколько вложенных компонентов и требуется передать что-то из родительского компонента в глубоко вложенный дочерний. Придётся передавать входные параметры вниз по всей цепочке компонентов, что иногда может быть очень неудобно.

В таких случаях можно использовать пару provide и inject. Родительские компоненты могут служить провайдерами зависимостей для всех своих потомков, независимо от того, насколько глубокая иерархия компонентов. Работа этой возможности состоит из двух частей: родительский компонент имеет опцию provide для предоставления данных, а дочерний компонент имеет опцию inject для внедрения и использования этих данных.

Схема provide/inject

Например, в такой иерархии компонентов:

Root
└─ TodoList
   ├─ TodoItem
   └─ TodoListFooter
      ├─ ClearTodosButton
      └─ TodoListStatistics
1
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
  }
})
1
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: `
    ...
  `
})
1
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: `
    ...
  `
})
1
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
  }
})
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

Теперь любое изменение todos.length будет корректно отражаться в компонентах, где внедряется todoLength. Подробнее про computed в разделе Вычисляемые свойства и методы-наблюдатели, а про reactive provide/inject в разделе Composition API.