【Vue.js】【図解】emit、propsを使ったコンポーネント間のデータのやり取り

こんにちは。JJです。
久しぶりの投稿になります。
今回はVue.jsにおけるコンポーネント間のデータのやり取りについて紹介していきます。

はじめに

Vue.jsを利用したフロントエンドの開発を進めていく中で、複雑な機能であったり、反復性を有する機能の開発を行う際、1つのVueコンポーネントで実装するよりも機能の単位、粒度に応じて複数のコンポーネントに分解して実装すると思います。後者の方が、経験上、ソースコードの可読性やメンテナンス性、さらにVue.jsの特徴の一つであるコンポーネントの再利用性も生かすことができるので、大変便利です。
しかしながらこの場合、複数のコンポーネント間でのデータのやり取りというものが必要不可欠になります。開発経験の浅い方の場合、その扱いに最初は悩むことでしょう。

そこで今回は簡単な実装例も交えつつ、Vue.jsの重要な機能の1つである「props」と「emit」について取り上げます。このテクニックに慣れることで開発効率も随分と向上すると思います。ぜひ、マスターしましょう。

コンポーネント間のデータのやり取り

コンポーネント分割を行う際に片方のコンポーネントがもう片方のコンポーネントを取り込み(import)する時、importする側を「親(コンポーネント)」と呼び、される側を「子(コンポーネント)」と呼びます。importにより、親と子は図のように互いがノード(黒い直線)によって結ばれます。しかしこのままでは、親と子は互いのオプション(data, methods, computedなど)の情報や状態を把握・共有することができません。これらを外部のコンポーネントから参照したいとき、明示的な記述が必要になります。特に今回のテーマである「コンポーネント間のデータのやり取り」では「親から子」にdataを伝達する手段「props」、そして「子から親」にdataを伝達する手段「emit」が存在します。それでは下記にそれぞれの概念について述べてまいります。

親から子へ、props

親から子に「data」を伝達したいとき、親側でv-bind(:)を経由してデータを子に流します。バインドしているので、props(プロパティ)の値の変更を検知できます。図のケースではParent.vueからChild1.vue、Child2.vue、Child3.vueに対してデータを渡しており、Child4.vueにはpropsの機能を用いていないことを示しています。ここで子は親からのデータを受け取るとき、配列による列挙形式か、オブジェクトの配列として記述する方法の2種類存在します。後者は、プロパティに対して、型指定やバリデーション等、詳細な設定を指定できます。

まず配列による列挙について紹介いたします。下記のように簡潔に指定できる反面、それぞれのデータの情報が記載されていないため、子では「item」も「value」も同じように扱います。

こちらはオブジェクトによる列挙になります。こちらの方がより詳細に指定できるため、「item」と「value」の違いついて明記できます。

子から親へ、emit

子から親に対してデータを伝達させたい場合は、カスタムイベントを使用します。カスタムイベントを活用する場合、下記のように$emitと$onが一対として機能します。親コンポーネントから子のイベントを監視したい場合は「v-on:」または「@」によりイベント名(今回の場合「updateEvt」)を紐づけます。図のケースではChild3.vueで発したイベントトリガー$emitによりParent.vueにてそのイベントが$onにより実行される様子を表しています。これをソースコードで示すと次のようになります。

子コンポーネント(Child3.vue)

また$emitの第2引数、第3引数…と特定の変数を指定することで、子から親に対してその変数の値を受け取ることができます。このとき、受け手である親はイベントと紐づけたメソッドに対して、その引数を指定します。

子コンポーネント(Child3.vue)

親コンポーネント(Parent.vue)

実装例

ここではpropsやemitを用いた実装例を紹介します。ある生産ラインの生産実績をオペレータが入力する簡易画面を題材とします。オペレータは製品の生産本数を数え上げ、上図の画面に入力します。この時、製造された製品が良品であれば、左側の追加ボタンを押し、不良品であれば右側の追加ボタンを押します。

今回の開発環境はVue CLI(ver3.7)を使用します。Vue CLIを扱うNodeパッケージモジュール「@vue/cli」がインストールされていない場合、まずはこれを追加します。

正しくインストールされたか、バージョンを確認します。

新たにプロジェクト名「production-result」を生成します。この時の初期設定はデフォルトとします。

上記の手順が完了すれば、下準備は完了です。

ソースコード

下記にソースコードとして3つvueコンポーネントを示します。

ProductionResult.vue

Sticker.vue

このように記述することで、ProductionResult.vueにおけるcommitメソッドを省略することができます。

今回のケースでは規模も小さいため、十分に活用する余地があります。
しかし、公式のドキュメントに記述されている見解の通り、$parentや$childrenは非推奨とされています。
これらの多用は大規模開発になるにつれて、どのコンポーネントでどのようにdataが更新されているかという事実が不透明になるためです。
その結果としてコンポーネント間のやり取りにおいて可読性の低下を招いたり、
理解の混乱を引き起こす原因となる可能性があります。
使用には十分に吟味しましょう。

ここで重要なこととして、親がマスタとなるデータ(良品および不良品の数)を保持、管理しているということです。子はあくまで、親に対して受動的に作用する単位機能として見てみましょう。Counter.vueはあくまで数え上げるためのコンポーネントであり、Sticker.vueではpropsで受け取ったタイトルや値を表示するためのコンポーネントです。データは上流となる「親」から下流に至る「子」への一方向に流れていることに注意しましょう。

おわりに

今回はコンポーネント間のデータのやり取りの中でも最も基本的なpropsとemitについて紹介しました。基本的には上記の内容を押さえておけば、データのやり取りについては苦労されないかと思います。データの伝達手段は他にもVuexやlocalStorageなどが存在し、場合や目的によって使い分ける必要があります。これらも学習されることで、より汎用的なデータの伝達が可能になるかと思います。

最近の記事

  • 関連記事
  • おすすめ記事
  • 特集記事

アーカイブ

カテゴリー

PAGE TOP