はじめに
備忘録として整理しました。
ハンマーを持つと全てが釘に見えるので、引き出し増やす程度に見ていただければ幸いです。
後述するソースコードはvue2.6.14で動作確認しています。
Vue3のProvide / Inject
以下公式より引用
通常、親コンポーネントから子コンポーネントにデータを渡すとき、props を使います。深くネストされたいくつかのコンポーネントがあり、深い階層にあるコンポーネントが浅い階層にあるコンポーネントの何かしらのデータのみを必要としている構造を想像してください。この場合でも、鎖のように繋ったコンポーネント全体にプロパティを渡す必要がありますが、時にそれは面倒になります。
そのような場合は、provide と inject のペアを利用できます。コンポーネント階層の深さに関係なく、親コンポーネントは、そのすべての子階層へ依存関係を提供するプロバイダとして機能することができます。この機能は 2 つの機能からなります: 親コンポーネントは、データを提供するためのオプション provide を持ち、子コンポーネントはそのデータを利用するためのオプション inject を持っています。
Vue2系での書き方
Vue3からの機能のように見えますが、実はVue2系でも使うことができます。
そのまま使うと親コンポーネントで更新した値が子コンポーネントに反映されません。
リアクティブではないためです。
リアクティブに扱う方法1 インスタンスをprovideで渡してしまう
こちらの記事では親のインスタンスごと子供に渡しているのですが、記載の通りそこそこなんでもできてしまって注意が必要です。
もし孫から親世代に変更したい場合はインスタンスごと渡しているので直接、もしくはメソッドから変更できます。
なんでもできてしまうので、方法2の方がおすすめです。
リアクティブに扱う方法2 Object.definePropertyをprovideする
調べていたら見つけた動画です。英語ですが今回のやりたいことの解説をそのまましてくれています。
方法1と2のサンプルコード
せっかくなので、孫の変更を親にできないかためしてみました。
あんまりやると無法地帯になりそうですが、propsとemitが連鎖し続けるくらいならこっちのほうがわかりやすい、、、気がします。
ProviderCompを親世代、
KoCompを子世代、
MagoCompを孫世代としてます
<template>
<div>
<pre>oya: {{value}} | {{value2}}</pre>
<div>
<button @click="countup">Count-up</button>
<hr>
<KoComp />
</div>
</div>
</template>
<script>
import KoComp from "./KoComp";
export default {
data() {
return {
value: 0,
value2: 0,
};
},
components: { KoComp },
provide() {
const provideValue = {}
Object.defineProperty(provideValue, 'value2',{
enumerable:true,
get:()=>this.value2,
set:(val)=>{this.value2=val} // injectした子孫から影響をうけつけるため。渡すだけならset不要
})
return {
"oyaVM": this, // 方法1
provideValue // 方法2
};
},
methods: {
countup() {
this.value++;
},
},
}
</script>
<template>
<div>
<p> ko</p>
<hr>
<MagoComp />
</div>
</template>
<script>
import MagoComp from "./MagoComp";
export default {
components: { MagoComp }
}
</script>
<template>
<div>
<p> mago {{oyaVM.value}} | {{provideValue}}</p>
<button @click="countup">Count-up</button>
<button @click="countup2">Count-up2</button>
<hr>
</div>
</template>
<script>
export default {
inject: ["oyaVM", "provideValue"], // 方法1も2もどちらも
methods: {
countup() {
this.oyaVM.value++; // 方法1
},
countup2() {
this.provideValue.value2++ // 方法2
},
},
}
</script>
おわりに
コンポーネントを細かく分解していると階層が深くなって、だんだんPropsの多重バケツリレーが起きてしまいますがそれから解放してくれる便利な機能ですね。
ただデメリットとしては、どうデータがやり取りされているか、流れが追いにくいかもです。