コンポーネントは、現代のフロントエンドフレームワークで最も強力な機能ですが、コンポーネントのスコープは独立しています。では、異なるコンポーネント間でどのようにデータをやり取りするのでしょうか?
大規模な Web アプリケーションを構築するために、現代のフロントエンドフレームワークはすべてコンポーネントシステムを導入しており、テンプレートとロジックを再利用可能なコンポーネントとして抽象化することで、プロジェクトの開発効率と保守性を向上させることができます。しかし、コンポーネント間の通信は常に問題となっており、今日は Vue でのコンポーネント間通信について議論してみましょう。
props#
props を使用すると、親コンポーネントから子コンポーネントにデータを渡すことができます。prop はコンポーネントに定義されたカスタム属性であり、子コンポーネントで事前に props を宣言する必要があります。
// 子コンポーネント
<template>
<div>{{text}}</div>
</template>
<script>
export default {
name: 'Confirm',
props: {
text: {
type: String,
default: ''
},
}
}
</script>
// 親コンポーネント
<template>
<confirm :text="テキスト">
</template>
カスタムイベント#
カスタムイベントを使用することで、子コンポーネントから親コンポーネントにデータを渡すことができます。
// 子コンポーネント
<template>
<button @click="confirm">クリック</button>
</template>
<script>
export default {
name: 'Confirm',
methods: {
confirm() {
this.$emit('confirm',"データ")
}
}
}
</script>
// 親コンポーネント
<template>
<confirm @confirm="handleConfirm">
</template>
<script>
export default {
methods: {
confirm(data) {
console.log(data);
}
}
}
</script>
子コンポーネントでは、this.$emit を使用してカスタムイベントを発火させることができます。第 1 引数はカスタムイベントの名前であり、第 2 引数は渡すパラメータです(オプション)。親コンポーネントでは @を使用して子コンポーネントのカスタムイベントを監視します。
イベントバス#
props は親子コンポーネント間のデータのやり取りに適していますが、兄弟コンポーネント間の通信にはイベントバスの方法が使用できます。具体的な使用方法は以下の通りです:
eventBus.js を作成します:
import Vue from 'vue';
export default new Vue();
イベントを発火するコンポーネント:
import eventBus from './eventBus.js';
methods: {
onClick(event) {
eventBus.$emit('clickEvent', event.target);
}
}
イベントを監視するコンポーネント:
import eventBus from './eventBus.js';
methods: {
onClick(event) {
eventBus.$on('clickEvent', (data) => {
console.log(data);
});
}
}
eventBus のメソッドは、グローバルな状態管理を実現し、任意のコンポーネント間でデータを簡単にやり取りすることができます。
Vuex#
大規模な Web アプリケーションでは、上記のコンポーネント間通信方法では実際の要件を満たすことができない場合があります。Vue の公式ウェブサイトでは、Vuex というグローバルな状態管理ライブラリが提供されています。詳細は公式ドキュメントを参照してください。
$root と $parent#
各 new Vue インスタンスの子コンポーネントでは、ルートインスタンスに $root プロパティを使用してアクセスできます。すべての子コンポーネントは、このインスタンスをグローバルなストアとしてアクセスまたは使用することができます。$root と同様に、$parent は子コンポーネントから親コンポーネントのインスタンスにアクセスするために使用できます。したがって、$root または $parent を使用して子コンポーネント間でデータをやり取りすることができます。
イベントを発火するコンポーネント:
methods: {
onClick(event) {
this.$root.$emit('clickEvent', event.target);
}
}
イベントを監視するコンポーネント:
methods: {
onClick(event) {
this.$root.$on('clickEvent', (data) => {
console.log(data);
});
}
}
$children#
親コンポーネントは $children を使用して子コンポーネントにアクセスし、親子コンポーネント間で通信することができます。
this.$children は現在のインスタンスの直接の子コンポーネントを取得します。$children は順序を保証せず、リアクティブではありませんので注意が必要です。
$parent と $children を使用する際は注意が必要です。これらはコンポーネントへの緊急のアクセス手段として使用されることが主な目的です。親子コンポーネント間の通信には、props と events を使用することをお勧めします。
//親コンポーネント
this.$children[0].xx = xx;
$refs#
$refs を使用すると、子要素または子コンポーネントの参照を作成し、$refs を使用して子コンポーネントのメソッドを簡単に呼び出したり、子コンポーネントのデータを取得したりすることができます。
// 親コンポーネント
<template>
<base-input ref="usernameInput"></base-input>
</template>
<script>
export default {
methods: {
focus() {
this.$refs.usernameInput.focus();
}
}
}
</script>
// 子コンポーネント
<template>
<input ref="input">
</template>
<script>
export default {
methods: {
focus: function () {
this.$refs.input.focus()
}
}
}
</script>
$attrs と $listeners#
$attrs には、親コンポーネントから子コンポーネントに渡され、子コンポーネントの props で定義されていない属性(class と style を除く)が含まれています。$attrs は自動的に子コンポーネントのルート要素に追加されますが、コンポーネントのオプションで inheritAttrs: false を設定することで、コンポーネントのルート要素に属性を継承させないようにすることもできます。
//子コンポーネント
Vue.component('base-input', {
inheritAttrs: false,
props: ['label', 'value'],
template: `
<label>
{{ label }}
<input
v-bind="$attrs"
v-bind:value="value"
v-on:input="$emit('input', $event.target.value)"
>
</label>
`
})
//親コンポーネント
<base-input
v-model="username"
required
placeholder="ユーザー名を入力してください"
></base-input>
同様に、$listeners を使用すると、親コンポーネントから渡されたすべてのイベントリスナー関数を特定の子要素に割り当てることができます。
Vue.component('base-input', {
inheritAttrs: false,
props: ['label', 'value'],
computed: {
inputListeners: function () {
var vm = this
return Object.assign({},
{
input: function (event) {
vm.$emit('input', event.target.value)
}
}
)
}
},
template: `
<label>
{{ label }}
<input
v-bind="$attrs"
v-bind:value="value"
v-on="inputListeners"
>
</label>
`
})
provide/inject#
provide と inject は、Vue の依存性注入の実装です。provide オプションを使用すると、子孫コンポーネントに提供したいデータ / メソッドを指定することができます。
// データを提供するコンポーネント
<script>
export default {
provide() {
return {
form: this
};
},
}
</script>
その後、任意の子孫コンポーネントで、inject オプションを使用して、コンポーネントに追加したい特定のプロパティを受け取ることができます。
<script>
export default {
inject: ["form"],
methods: {
validate() {
console.log(this.form);
}
}
</script>
Vue の公式の状態管理ライブラリである Vuex の原理も provide/inject を使用して実装されています。
まとめ#
以上が Vue でのコンポーネント間通信の方法です。異なる方法は異なるシナリオに適しており、銀の弾はありません。プロジェクト開発では、要件に応じて適切な方法を選択する必要があります。