# Provide / inject
このページは既にコンポーネントの基本を読んでいる事を前提としています。 コンポーネントを初めて使う方はそちらを先にお読みください。
通常、親コンポーネントから子コンポーネントにデータを渡すとき、props を使います。深くネストされたいくつかのコンポーネントがあり、深い階層にあるコンポーネントが浅い階層にあるコンポーネントの何かしらのデータのみを必要としている構造を想像してください。この場合でも、鎖のように繋ったコンポーネント全体にプロパティを渡す必要がありますが、時にそれは面倒になります。
そのような場合は、provide
と inject
のペアを利用できます。コンポーネント階層の深さに関係なく、親コンポーネントは、そのすべての子階層へ依存関係を提供するプロバイダとして機能することができます。この機能は2つの機能からなります: 親コンポーネントは、データを提供するためのオプション provide
を持ち、子コンポーネントはそのデータを利用するためのオプション inject
を持っています。
例えば、このような構造があるとします:
Root
└─ TodoList
├─ TodoItem
└─ TodoListFooter
├─ ClearTodosButton
└─ TodoListStatistics
2
3
4
5
6
もし todo-items のサイズを 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 }}
<!-- rest of the template -->
</div>
`
})
app.component('todo-list-statistics', {
inject: ['user'],
created() {
console.log(`Injected property: ${this.user}`) // > Injected property: 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 // this will result in error `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
こうすることで、子コンポーネントが依存している何かを変更したり削除したりしてしまうことを恐れることなく、より安全にコンポーネントを開発し続けることができます。これらのコンポーネント間のインターフェースは、props と同じく、明確に定義されています。
実際、依存関係の注入は、いわば「長距離な props 」のように考えることができます。後述の点を除きます:
- 親コンポーネントは、提供したプロパティを子孫コンポーネントのどこで使用しているか知る必要がありません
- 子コンポーネントは、注入されたプロパティがどこから提供されたものなのか知る必要がありません
# リアクティブと連携する
前述の例では、リスト todos
を変更しても、その変更は注入された todoLength
には反映されません。これは、provide/inject
の束縛( binding )がデフォルトでリアクティブ でない ことが原因です。ref
で定義されたプロパティや reactive
で作成されたオブジェクトを provide
に渡すことにより、この振る舞いを変更することができます。この場合、祖先コンポーネントをリアクティブにするためには、Composition API の computed
で定義したプロパティを todoLength
を割り当てる必要があります。
app.component('todo-list', {
// ...
provide() {
return {
todoLength: Vue.computed(() => this.todos.length)
}
}
})
app.component('todo-list-statistics', {
inject: ['todoLength'],
created() {
console.log(`Injected property: ${this.todoLength.value}`) // > Injected property: 5
}
})
2
3
4
5
6
7
8
9
10
11
12
13
14
15
こうすると、todos.length
へのあらゆる変更は、todoLength
が注入されたコンポーネントに正しく反映されます。computed
については、 算出プロパティとウォッチのセクション を、そして reactive
の provide/inject の詳細については、Composition API のセクション をご覧ください。
← スロット 動的 & 非同期コンポーネント →