はじめに
Vue.jsでコンポーネント間のデータ受け渡しは基本的に、プロパティとカスタムイベント(Props/Emit)を利用して行われている。
しかし、「コンポーネントの中にコンポーネントを読んで、そのコンポーネントの中でコンポーネントを読んで、そのコン・・・・・」
みたいな場合に、
最上位コンポーネントから、最下位コンポーネントまでデータを渡そうとした時に壮絶なデータのバケツリレーが実装することになります。
そのような場合はProvide/Injectを使うとデータのバケツリレーせずに実装することが可能です。
その方法を学習したので、自分用のメモとしてQiitaに残そうと思います。
例
例として以下の「Parent」「Myself」「Child」コンポーネントを用意します。
この三つのコンポーネントの関係は「MyselfはParentのコンポーネント」、「ChildはMyselfのコンポーネント」です。
See the Pen Untitled by TakayukiTakase (@takayuki332) on CodePen.
説明
上記例により最上位コンポーネントと最下位コンポーネント間でバケツリレーすることなくデータをやり取りすることができました。
Parentコンポーネントのprovide
provideを設定することで自分より下位のコンポーネントに対してデータや関数を提供することができます。
provide() {
return {
// dataで設定したtitleをリアクティブに提供する
title: Vue.computed(() => this.title),
// 関数も提供して下位コンポーネントで提供関数を実行することができる。
// 本例では引数を通して下位コンポーネントからデータを取得するようにしています。
updateTitle: this.updateTitle,
};
},
上記の「Vue.computed(() => this.title)」を「this.title」だけにすると初期値の「hello」は表示されるのですが、
入力された値がリアクティブに変化しません。
Childコンポーネントのinject
injectを指定することで自分より上位のコンポーネントでprovideされたデータや関数を利用することができます。
inject: ['title', 'updateTitle'],
こうすることで以下のように利用することができます。
template: `
<div id="child">
<!-- updateTitleの引数により上位コンポーネントへデータを渡す -->
<input type="button" value="更新" v-on:click="updateTitle('Childからデータセット')" />
{{ title.value }}
</div>
`,
ちなみにここで「title.value」ではなく「title」と指定すると「"hello"」とダブルクォーテーションで囲まれて表示されてしまいます。
なぜだろう?と思いtypeofで確認してみると型が「object」になっていました。
なのでinjectのデータを利用する場合は「.value」とつけた方が無難だと思います。
バケツリレーもやってみる(Props/Emit)
See the Pen Qiita vue jsのバケツリレー by TakayukiTakase (@takayuki332) on CodePen.
バケツリレーがうまく動くと気持ちがいいですが、保守などが大変そうですね。
コンポーネントの階層が多くなればなるほどその傾向にあると思います。
Provide/Injectをいつ使う?
私自身探り探りではありますが、書籍で調べた限り、親和性の強いコンポーネントに対して使えばいいと思います。
最下位コンポーネントは他の違うくくりのコンポーネントでも利用されていることがあり、再利用しづらくなってしまうからです。
参考