# 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: ['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
  }
})
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: ['Feed a cat', 'Buy tickets']
    }
  },
  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: ['Feed a cat', 'Buy tickets']
    }
  },
  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 не реактивны по умолчанию. Можно изменить это поведение, передав свойство 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
  }
})
1
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.

Deployed on Netlify.
Последнее обновление: 2020-12-12, 13:34:06 UTC